From d33a36ee51d4d9ea1974cc56ba7ac5a8f3211ce7 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Wed, 3 Jan 2024 18:02:51 +0530 Subject: [PATCH 01/11] =?UTF-8?q?=E2=9D=8E=20Remove=20note=20about=20Windo?= =?UTF-8?q?ws-arm64=20at=20this=20moment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 7564fda..8051b1b 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,6 @@ For more information on using Hugo and its command-line interface, please refer ## Supported platforms - | Platform | Architecture | Supported | | -------- | ------------ | ---------------- | | macOS | x86_64 | ✅ | @@ -60,7 +59,6 @@ For more information on using Hugo and its command-line interface, please refer | Linux | amd64 | ✅ | | Linux | arm64 | Coming soon | | Windows | x86_64 | ✅ | -| Windows | arm64 | Coming soon | ## Building from source From 98a2a387362482fc3016e6b1c5b0d0e6ef751659 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 4 Jan 2024 04:37:22 +0530 Subject: [PATCH 02/11] =?UTF-8?q?=F0=9F=8F=B3=EF=B8=8F=20Add=20whitespace?= =?UTF-8?q?=20to=20Hugo=20license?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- licenses/LICENSE-hugo.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/licenses/LICENSE-hugo.txt b/licenses/LICENSE-hugo.txt index 3a8e444..ad3850b 100644 --- a/licenses/LICENSE-hugo.txt +++ b/licenses/LICENSE-hugo.txt @@ -198,4 +198,4 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file + limitations under the License. From ffeea3e7b74079d16647ec364c569e782a911b88 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 4 Jan 2024 15:27:19 +0530 Subject: [PATCH 03/11] =?UTF-8?q?=E2=9C=85=20Don't=20git-ignore=20the=20`b?= =?UTF-8?q?inaries/`=20folder?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- python_hugo/binaries/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 python_hugo/binaries/.gitignore diff --git a/python_hugo/binaries/.gitignore b/python_hugo/binaries/.gitignore new file mode 100644 index 0000000..f935021 --- /dev/null +++ b/python_hugo/binaries/.gitignore @@ -0,0 +1 @@ +!.gitignore From 85ab1c9ee4a80c9f18cf028c90a749e06f5e881c Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 4 Jan 2024 18:20:21 +0530 Subject: [PATCH 04/11] =?UTF-8?q?=E2=9C=A8=20Remove=20note=20about=20need?= =?UTF-8?q?=20for=20virtual=20environment=20for=20Hugo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 8051b1b..e8b34f2 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,6 @@ pip install hugo-python This places a `hugo` executable in a `binaries` directory in your virtual environment and adds an entry point to it. -> [!IMPORTANT] -> It is currently necessary to use a virtual environment to install and use isolated version of Hugo. Please refer to https://github.com/agriyakhetarpal/hugo-python-distributions/issues/7 - > [!TIP] > You can, however, use [`pipx`](https://github.com/pypa/pipx) to install Hugo in an isolated environment without having to create a virtual environment manually, allowing you to use Hugo as a command-line tool without having to install it globally on your system. Please refer to the [`pipx` documentation](https://pipx.pypa.io/stable/) for more information. From 5871a8abb6b7eba54fe7ba941bd095400de33166 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 4 Jan 2024 18:22:02 +0530 Subject: [PATCH 05/11] =?UTF-8?q?=E2=9A=9B=EF=B8=8F=20Amend=20note=20about?= =?UTF-8?q?=20using=20`pipx`=20to=20install=20Hugo=20binary?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e8b34f2..26e1838 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,14 @@ pip install hugo-python This places a `hugo` executable in a `binaries` directory in your virtual environment and adds an entry point to it. +Alternatively, you can install the package globally on your system: + +```bash +pip3 install hugo-python +``` + > [!TIP] -> You can, however, use [`pipx`](https://github.com/pypa/pipx) to install Hugo in an isolated environment without having to create a virtual environment manually, allowing you to use Hugo as a command-line tool without having to install it globally on your system. Please refer to the [`pipx` documentation](https://pipx.pypa.io/stable/) for more information. +> It is a great idea to use [`pipx`](https://github.com/pypa/pipx) to install Hugo in an isolated location without having to create a virtual environment, which will allow you to use Hugo as a command-line tool without having to install it globally on your system. Please refer to the [`pipx` documentation](https://pipx.pypa.io/stable/) for more information. Then, you can use the `hugo` commands as you would normally: From 241264f619f34bfc45d132292d5d225f61d688a4 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 4 Jan 2024 22:30:52 +0530 Subject: [PATCH 06/11] =?UTF-8?q?=F0=9F=9B=B3=EF=B8=8F=20Establish=20CLI?= =?UTF-8?q?=20for=20entry=20point=20instead=20of=20`=5F=5Finit=5F=5F.py`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- python_hugo/__init__.py | 65 +++-------------------------------------- python_hugo/cli.py | 46 +++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 61 deletions(-) create mode 100644 python_hugo/cli.py diff --git a/python_hugo/__init__.py b/python_hugo/__init__.py index 16bcf71..abdf26f 100644 --- a/python_hugo/__init__.py +++ b/python_hugo/__init__.py @@ -6,65 +6,8 @@ from __future__ import annotations -import os -import sys +from python_hugo.cli import __call -import importlib.metadata - -HUGO_VERSION = importlib.metadata.version("python-hugo") -FILE_EXT = ".exe" if sys.platform == "win32" else "" - -# On editable and source installs, we keep binaries in the package directory -# for ease of use. On wheel installs, we keep them in the venv/binaries directory. -# On installing from a wheel, the binary is in the venv/binaries, but the package -# is in venv/lib/python3.X/site-packages, so we need to go up two directories and -# then down into binaries -# Note: on Windows, this is venv\Lib\site-packages (instead of venv/lib/python3.X/site-packages) -# therefore we need to go up to the venv directory and then down into the data files -try: - hugo_executable = os.path.join( - os.path.dirname(__file__), - "binaries", - f"hugo-{HUGO_VERSION}" + FILE_EXT, - ) - if not os.path.exists(hugo_executable): - raise FileNotFoundError -except FileNotFoundError: - if sys.platform == "win32": - PATH_TO_SEARCH = os.path.join( - os.path.dirname( - os.path.dirname( - os.path.dirname( - os.path.dirname(__file__) - ) - ) - ), - "binaries" - ) # four times instead of five - else: - # five times instead of four - PATH_TO_SEARCH = os.path.join( - os.path.dirname( - os.path.dirname( - os.path.dirname( - os.path.dirname( - os.path.dirname(__file__) - ) - ) - ) - ), - "binaries" - ) - - # Go up into the venv directory and down into the data files - hugo_executable = os.path.join(PATH_TO_SEARCH, f"hugo-{HUGO_VERSION}" + FILE_EXT) - if not os.path.exists(hugo_executable): - raise FileNotFoundError from None -except Exception as e: - sys.exit(f"Error: {e}") - -def __call(): - """ - Hugo binary entry point. Passes all command-line arguments to Hugo. - """ - os.execvp(hugo_executable, ["hugo", *sys.argv[1:]]) +# Hugo binary entry point caller +if __name__ == "__main__": + __call() diff --git a/python_hugo/cli.py b/python_hugo/cli.py new file mode 100644 index 0000000..b61ac9e --- /dev/null +++ b/python_hugo/cli.py @@ -0,0 +1,46 @@ +""" +Copyright (c) 2023 Agriya Khetarpal. All rights reserved. + +python-hugo: Binaries for the Hugo static site generator, installable with pip +""" + +from __future__ import annotations + +import importlib.metadata +import os +import platform +import sys +from functools import lru_cache + +HUGO_VERSION = importlib.metadata.version("python-hugo") +FILE_EXT = ".exe" if sys.platform == "win32" else "" +HUGO_PLATFORM = { + "darwin": "darwin", + "linux": "linux", + "win32": "windows", +}[sys.platform] +HUGO_ARCH = { + "x86_64": "amd64", + "arm64": "arm64", + "AMD64": "amd64", + "aarch64": "arm64", +}[platform.machine()] + + +@lru_cache(maxsize=1) +def hugo_executable(): + """ + Returns the path to the Hugo executable. + """ + return os.path.join( + os.path.dirname(__file__), + "binaries", + f"hugo-{HUGO_VERSION}-{HUGO_PLATFORM}-{HUGO_ARCH}" + FILE_EXT, + ) + + +def __call(): + """ + Hugo binary entry point. Passes all command-line arguments to Hugo. + """ + os.execvp(hugo_executable(), ["hugo", *sys.argv[1:]]) From 4ed43fcbb05b9f6603312fa2876551b109a3cca4 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 4 Jan 2024 23:36:00 +0530 Subject: [PATCH 07/11] =?UTF-8?q?=F0=9F=93=8C=20Pin=20`wheel=3D=3D0.42.0`?= =?UTF-8?q?=20to=20avoid=20API=20discrepancies?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8a2df53..2ba06b4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools>=61", "pooch", "tqdm", "wheel"] +requires = ["setuptools>=64", "pooch", "tqdm", "wheel==0.42.0"] build-backend = "setuptools.build_meta" [project] From f5ae1d5268dfe8b40dfc8ed60ff34cf49fbae3e9 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 4 Jan 2024 23:46:10 +0530 Subject: [PATCH 08/11] =?UTF-8?q?=E2=9A=94=EF=B8=8F=20Add=20notes=20about?= =?UTF-8?q?=20cross-compiling=20(macOS=20only)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 26e1838..3aa713c 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ For more information on using Hugo and its command-line interface, please refer | Platform | Architecture | Supported | | -------- | ------------ | ---------------- | | macOS | x86_64 | ✅ | -| macOS | arm64 | Coming soon | +| macOS | arm64 | ✅ | | Linux | amd64 | ✅ | | Linux | arm64 | Coming soon | | Windows | x86_64 | ✅ | @@ -88,6 +88,27 @@ pip install -e . # editable installation pip install . # regular installation ``` +### Cross-compiling for different architectures + +> [!NOTE] +> This functionality is implemented just for macOS at the moment, but it can be extended to other platforms as well in the near future. + +This package is capable of cross-compiling Hugo binaries for the same platform but different architectures and it can be used as follows. + +Say, on an Intel-based (x86_64) macOS machine: + +```bash +export GOARCH="arm64" +pip install . # or pip install -e . +``` + +This will build a macOS arm64 binary distribution of Hugo that can be used on Apple Silicon-based (arm64) macOS machines. To build a binary distribution for the _target_ Intel-based (x86_64) macOS platform on the _host_ Apple Silicon-based (arm64) macOS machine, you can use the following command: + +```bash +export GOARCH="amd64" +pip install . # or pip install -e . +``` + ## Background Binaries for the Hugo static site generator are available for download from the [Hugo releases page](https://github.com/gohugoio/hugo/releases). These binaries have to be downloaded and placed in an appropriate location on the system manually and the PATH environment variable has to be updated to include said location. From ef3f2579586f915c4a75d88e17b6ccf568b67ca8 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Fri, 5 Jan 2024 00:17:12 +0530 Subject: [PATCH 09/11] =?UTF-8?q?=F0=9F=9B=9F=20Configure=20cache=20to=20h?= =?UTF-8?q?ave=20different=20save=20and=20restore=20points?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1f31a10..4837d2e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,7 +51,7 @@ jobs: run: choco install mingw - name: Restore Hugo builder cache - uses: actions/cache@v3.3.2 + uses: actions/cache/restore@v3 with: path: ./hugo_cache/ key: ${{ runner.os }}-${{ matrix.go-version }}-hugo-build-cache-${{ hashFiles('**/setup.py', '**/pyproject.toml') }} @@ -63,5 +63,11 @@ jobs: run: | python -m build --wheel . --outdir dist/ + - name: Save Hugo builder cache + uses: actions/cache/save@v3 + with: + path: ./hugo_cache/ + key: ${{ runner.os }}-${{ matrix.go-version }}-hugo-build-cache-${{ hashFiles('**/setup.py', '**/pyproject.toml') }} + - name: Test entry points for package run: nox -s venv From f63f79791af2d862f3882f78c20d082f9c2ab799 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 4 Jan 2024 23:36:37 +0530 Subject: [PATCH 10/11] =?UTF-8?q?=F0=9F=94=A5=20Major=20packaging=20revamp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.py | 257 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 226 insertions(+), 31 deletions(-) diff --git a/setup.py b/setup.py index 0b069ad..55e9a8e 100644 --- a/setup.py +++ b/setup.py @@ -1,12 +1,17 @@ +import glob import os -import sys +import platform +import shutil import subprocess +import sys import tarfile - -from setuptools import setup, Extension -from setuptools.command.build_ext import build_ext +from pathlib import Path import pooch +from setuptools import Command, Extension, setup +from setuptools.command.build_ext import build_ext +from wheel.bdist_wheel import bdist_wheel, get_platform +from wheel.macosx_libfile import calculate_macosx_platform_tag # Keep in sync with pyproject.toml and update SHA-256 hashes accordingly HUGO_VERSION = "0.120.4" @@ -14,25 +19,58 @@ f"https://github.com/gohugoio/hugo/archive/refs/tags/v{HUGO_VERSION}.tar.gz" ) -# Pooch will download the tarball into the OS cache directory. +# The pooch tool will download the tarball into the hugo_cache/ directory. # We will point the build command to that location to build Hugo from source HUGO_CACHE_DIR = "hugo_cache" HUGO_SHA256 = "e374effe369c340d8085060e6bb45337eabf64cfe075295432ecafd6d033eb8b" FILE_EXT = ".exe" if sys.platform == "win32" else "" +# Normalise platform strings to match the Go toolchain +HUGO_PLATFORM = { + "darwin": "darwin", + "linux": "linux", + "win32": "windows", +}[sys.platform] + +# Normalise architecture strings to match the Go toolchain +HUGO_ARCH = { + "x86_64": "amd64", + "arm64": "arm64", + "AMD64": "amd64", + "aarch64": "arm64", +}[platform.machine()] + class HugoBuilder(build_ext): """ - Custom build_ext command that builds Hugo from source + Custom extension command that builds Hugo from source, placing the binary into + the package directory for further use. """ def initialize_options(self): super().initialize_options() self.hugo_version = None + self.hugo_platform = None + self.hugo_arch = None def finalize_options(self): + # Platforms and architectures that we will build Hugo for are: + # i.e., a subset of "go tool dist list": + # 1. darwin/amd64 + # 2. darwin/arm64 + # 3. linux/amd64 + # 4. linux/arm64 + # 5. windows/amd64 + # The platform is the first part of the string, the architecture is the second. + # We need to mangle the hugo binary name to include the platform and architecture + # so that we can build Hugo for multiple platforms and architectures. + # The platform is used to set the GOOS environment variable, the architecture + # is used to set the GOARCH environment variable, and they must be exactly these + # strings for the Go toolchain to work. super().finalize_options() self.hugo_version = HUGO_VERSION + self.hugo_platform = HUGO_PLATFORM + self.hugo_arch = HUGO_ARCH def run(self): """ @@ -40,7 +78,7 @@ def run(self): # the name so that it is unique to the verion of Hugo being built. """ - # Download Hugo source tarball, place into OS cache directory + # Download Hugo source tarball, place into hugo_cache/ directory hugo_targz = pooch.retrieve( url=HUGO_RELEASE, known_hash=HUGO_SHA256, @@ -48,18 +86,28 @@ def run(self): progressbar=True, ) - # Extract Hugo source tarball into a folder of the same name in the OS cache directory + # Extract Hugo source tarball into a folder hugo-HUGO_VERSION/ + # inside hugo_cache/ with tarfile.open(hugo_targz) as tar: tar.extractall(path=HUGO_CACHE_DIR) - # The binary is put into GOBIN, which is set to the package directory (python_hugo/binaries) - # for use in editable mode. The binary is copied into the wheel afterwards + # The binary is put into GOBIN, which is set to the package directory + # (python_hugo/binaries/) for use in editable mode. The binary is copied + # into the wheel afterwards + # Error: GOBIN cannot be set if GOPATH is set when compiling for different + # architectures, so we use the default GOPATH/bin as the place to copy + # binaries from + # os.environ["GOBIN"] = os.path.join( + # os.path.dirname(os.path.abspath(__file__)), "python_hugo", "binaries" + # ) os.environ["CGO_ENABLED"] = "1" - os.environ["GOBIN"] = os.path.join( - os.path.dirname(os.path.abspath(__file__)), "python_hugo", "binaries" - ) + os.environ["GOPATH"] = os.path.abspath( + HUGO_CACHE_DIR + ) # must be absolute (Go requirement) - os.environ["GOPATH"] = os.path.abspath(HUGO_CACHE_DIR) + os.environ["GOOS"] = self.hugo_platform + os.environ["GOARCH"] = os.environ.get("GOARCH", self.hugo_arch) + # i.e., allow override if GOARCH is set! # Build Hugo from source using the Go toolchain, place it into GOBIN # Requires the following dependencies: @@ -69,6 +117,12 @@ def run(self): # 3. Git # # Once built this the files are cached into GOPATH for future use + + # Delete hugo_cache/bin/ + files inside, it left over from a previous build + shutil.rmtree( + os.path.join(os.path.abspath(HUGO_CACHE_DIR), "bin"), ignore_errors=True + ) + subprocess.check_call( [ "go", @@ -80,28 +134,169 @@ def run(self): ) # TODO: introduce some error handling here to detect compilers, etc. - # Mangle the name of the compiled executable to include the version - # of Hugo being built - original_name = os.path.join(os.environ.get("GOBIN"), "hugo" + FILE_EXT) + # Mangle the name of the compiled executable to include the version, the + # platform, and the architecture of Hugo being built. + # The binary is present in GOPATH (i.e, either at hugo_cache/bin/ or at + # hugo_cache/bin/$GOOS_$GOARCH/bin) and now GOBIN is not set, so we need + # to copy it from there. + + # If the GOARCH is not the same as self.hugo_arch, we are cross-compiling, so + # we need to go into the GOOS_GOARCH/bin folder to find the binary rather than + # the GOPATH/bin folder. + + if os.environ.get("GOARCH") != self.hugo_arch: + original_name = os.path.join( + os.environ.get("GOPATH"), + "bin", + f"{self.hugo_platform}_{os.environ.get('GOARCH')}", + "hugo" + FILE_EXT, + ) + else: + original_name = os.path.join( + os.environ.get("GOPATH"), "bin", "hugo" + FILE_EXT + ) + new_name = os.path.join( - os.environ.get("GOBIN"), f"hugo-{HUGO_VERSION}" + FILE_EXT + os.environ.get("GOPATH"), + "bin", + f"hugo-{HUGO_VERSION}-{self.hugo_platform}-{os.environ.get('GOARCH', self.hugo_arch)}" + + FILE_EXT, ) os.rename(original_name, new_name) + # Copy the new_name file into a folder binaries/ inside python_hugo/ + # so that it is included in the wheel. + # basically we are copying hugo-HUGO_VERSION-PLATFORM-ARCH into + # python_hugo/binaries/ and creating the folder if it does not exist. + + binaries_dir = os.path.join( + os.path.dirname(__file__), "python_hugo", "binaries" + ) + if not os.path.exists(binaries_dir): + os.mkdir(binaries_dir) + + # if the binary already exists, delete it, and then copy the new binary + # to ensure that the binary is always the newest rendition + if os.path.exists(os.path.join(binaries_dir, os.path.basename(new_name))): + os.remove(os.path.join(binaries_dir, os.path.basename(new_name))) + os.rename(new_name, os.path.join(binaries_dir, os.path.basename(new_name))) + + +# https://github.com/pypa/setuptools/issues/1347: setuptools does not support +# the clean command from distutils yet. so we need to use a workaround that gets +# called inside bdist_wheel invocation. +class Cleaner(Command): + """ + Custom command that cleans the build directory of the package at the project root. + """ + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + """Clean ancillary files at runtime.""" + + here = os.path.normpath(os.path.abspath(os.path.dirname(__file__))) + files_to_clean = "./build ./*.pyc ./*.egg-info ./__pycache__".split(" ") + + for path_spec in files_to_clean: + # Make paths absolute and relative to this path + abs_paths = glob.glob( + os.path.normpath(os.path.join(here, path_spec)) + ) + for path in [str(p) for p in abs_paths]: + if not path.startswith(here): + # raise error if path in files_to_clean is absolute + outside + # this directory + msg = f"{path} is not a path around {here}" + raise ValueError(msg) + shutil.rmtree(path) + + +# Mock setuptools into thinking that we are building a target binary on a host machine +# so that the wheel gets tagged correctly. We can fuse the arm64 and amd64 wheels +# together later using delocate. +class HugoWheel(bdist_wheel): + """ + A customised wheel build command that sets the platform tags to accommodate + the varieties of the GOARCH and GOOS environment variables when cross-compiling + the Hugo binary. Currently used for macOS arm64 and macOS x86_64. + """ + + def initialize_options(self): + super().initialize_options() + + def finalize_options(self): + # plat_name is essentially the {platform tag} at the end of the wheel name. + # Note to self: the wheel name will look like this: + # {distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl + # # on macOS, if GOARCH is set to arm64 on an x86_64 machine, or if GOARCH is set to + # amd64 on an arm64 machine, we need to set the platform tag to macosx_X_Y_arm64 or + # macosx_X_Y_x86_64 respectively. + # + # + # TODO: FIXME: look at how Linux and Windows tags are set later + + if sys.platform == "darwin": + platform_tag = get_platform("_") + # ensure correct platform tag for macOS arm64 and macOS x86_64 + if "arm64" in platform_tag and os.environ.get("GOARCH") == "amd64": + self.plat_name = platform_tag.replace("arm64", "x86_64") + if "x86_64" in platform_tag and os.environ.get("GOARCH") == "arm64": + self.plat_name = platform_tag.replace("x86_64", "arm64") + super().finalize_options() + + def run(self): + self.root_is_pure = False # ensure that the wheel is tagged as a binary wheel + + self.run_command("clean") # clean the build directory before building the wheel + + # ensure that the binary is copied into the binaries/ folder and then into the wheel. + hugo_binary = os.path.join( + os.path.dirname(__file__), + "python_hugo", + "binaries", + f"hugo-{HUGO_VERSION}-{HUGO_PLATFORM}-{os.environ.get('GOARCH', HUGO_ARCH)}" + + FILE_EXT, + ) + + # if the binary does not exist, then we need to build it, so invoke + # the build_ext command again and proceed to build the binary + if not os.path.exists(hugo_binary): + self.run_command("build_ext") + + # now that the binary exists, we have ensured its presence in the wheel + super().run() + setup( - ext_modules=[Extension(name="hugo.build", sources=["setup.py"])], - cmdclass={"build_ext": HugoBuilder}, - packages=["python_hugo", "python_hugo.binaries"] - if os.path.exists("python_hugo/binaries") - else ["python_hugo"], - # Include binary named hugo-HUGO_VERSION in the wheel, which is presently stored - # in python_hugo/binaries (i.e., in a folder binaries/ inside the package directory) - package_data={"python_hugo": ["binaries/*"]}, - # include_package_data=True, - # TODO: data_files is deprecated for wheels, so we need to find a better way to - # include the binary in the wheel - data_files=[("binaries", [f"python_hugo/binaries/hugo-{HUGO_VERSION}" + FILE_EXT])], - entry_points={"console_scripts": ["hugo=python_hugo.__init__:__call"]}, + ext_modules=[ + Extension( + name="hugo.build", + sources=[ + f"python_hugo/binaries/hugo-{HUGO_VERSION}-{HUGO_PLATFORM}-{os.environ.get('GOARCH', HUGO_ARCH)}" + + FILE_EXT + ], + ) + ], + cmdclass={ + "build_ext": HugoBuilder, + "clean": Cleaner, + "bdist_wheel": HugoWheel, + }, + packages=["python_hugo", "python_hugo.binaries"], + package_data={ + "python_hugo": [ + f"binaries/hugo-{HUGO_VERSION}-{HUGO_PLATFORM}-{os.environ.get('GOARCH', HUGO_ARCH)}" + + FILE_EXT + ] + }, + include_package_data=True, + entry_points={"console_scripts": ["hugo=python_hugo.cli:__call"]}, + # the package version, which is the same as the version + # of Hugo that it will build and provide version=HUGO_VERSION, ) From e1dc1ca95107bf6598748061a65d06ea447d4fbd Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Fri, 5 Jan 2024 02:44:35 +0530 Subject: [PATCH 11/11] =?UTF-8?q?=F0=9F=8F=8B=EF=B8=8F=E2=80=8D=E2=99=80?= =?UTF-8?q?=EF=B8=8F=20Exercise=20new=20cache=20rules?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit