From a6e95bf0b4e260ddb16ed2af42d8c882b90d2cc1 Mon Sep 17 00:00:00 2001 From: Michael D Starch Date: Tue, 31 May 2022 20:52:43 -0700 Subject: [PATCH 01/13] adding initial build-native action --- .github/workflows/build-native.yml | 31 ++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/workflows/build-native.yml diff --git a/.github/workflows/build-native.yml b/.github/workflows/build-native.yml new file mode 100644 index 000000000..bc7f0a7d2 --- /dev/null +++ b/.github/workflows/build-native.yml @@ -0,0 +1,31 @@ +name: Build Native Images +on: + create: + tags: + - v* + push: + branch: ["main"] +jobs: + Build-Native-Images: + strategy: + matrix: + os: ["macos-latest", "ubuntu-latest"] + runs-on: ${{ matrix.os }} + steps: + - name: "Checkout F´ Repository" + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Setup Graal VM and PATH + run: | + echo "${GRAALVM_11_ROOT}" >> $GITHUB_PATH + - name: Image Build Step + working-directory: ./compiler + run: GRAALVM_JAVA_HOME="${GRAALVM_11_ROOT}" ./release + # Archive the outputs + - name: 'Archive Tar' + uses: actions/upload-artifact@v2 + with: + name: images-${{ matrix.os }} + path: compiler/fpp-native*.tar.gz + retention-days: 5 From 6c3fcda067a9eedf5610fd0895bfffe0d58da10b Mon Sep 17 00:00:00 2001 From: Michael D Starch Date: Thu, 2 Jun 2022 09:49:52 -0700 Subject: [PATCH 02/13] reproducable GRAAL and publishing of binaries --- .github/workflows/build-native.yml | 27 ++++++++++----------------- .github/workflows/env-setup | 19 +++++++++++++++++++ .github/workflows/publish | 16 ++++++++++++++++ compiler/release | 2 +- 4 files changed, 46 insertions(+), 18 deletions(-) create mode 100755 .github/workflows/env-setup create mode 100755 .github/workflows/publish diff --git a/.github/workflows/build-native.yml b/.github/workflows/build-native.yml index bc7f0a7d2..8c799ad77 100644 --- a/.github/workflows/build-native.yml +++ b/.github/workflows/build-native.yml @@ -1,31 +1,24 @@ name: Build Native Images on: - create: - tags: - - v* - push: - branch: ["main"] + release: + types: [published] jobs: Build-Native-Images: strategy: matrix: - os: ["macos-latest", "ubuntu-latest"] + os: ["ubuntu-latest", "macos-latest"] runs-on: ${{ matrix.os }} steps: - name: "Checkout F´ Repository" uses: actions/checkout@v2 with: fetch-depth: 0 - - name: Setup Graal VM and PATH - run: | - echo "${GRAALVM_11_ROOT}" >> $GITHUB_PATH - name: Image Build Step working-directory: ./compiler - run: GRAALVM_JAVA_HOME="${GRAALVM_11_ROOT}" ./release - # Archive the outputs - - name: 'Archive Tar' - uses: actions/upload-artifact@v2 - with: - name: images-${{ matrix.os }} - path: compiler/fpp-native*.tar.gz - retention-days: 5 + run: | + GRAALVM_JAVA_HOME="$( ${GITHUB_WORKSPACE}/.github/workflows/env-setup )" ./release + mv native-fpp-*.tar.gz "${GITHUB_WORKSPACE}" + - name: Publish Release Binaries + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: ${GITHUB_WORKSPACE}/.github/workflows/publish native-fpp-*.tar.gz diff --git a/.github/workflows/env-setup b/.github/workflows/env-setup new file mode 100755 index 000000000..6eed9442d --- /dev/null +++ b/.github/workflows/env-setup @@ -0,0 +1,19 @@ +#!/bin/bash +graal_ver="22.1.0" +graal_os="$( uname -s | tr "[:upper:]" "[:lower:]")" +graal_ar="graalvm-ce-java11-${graal_os}-amd64-${graal_ver}.tar.gz" +graal_dir="graalvm-ce-java11-${graal_ver}" +graal_url="https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-${graal_ver}/${graal_ar}" + +# Download Graal +curl -L "${graal_url}" | tar -xz +graal_bin="${graal_dir}/bin" +if [ ! -d "${graal_bin}" ] +then + graal_bin="${graal_dir}/Contents/Home/bin" +fi +# Install native image +${graal_bin}/gu install native-image 1>&2 + +# Return Graal home directory +echo "$(dirname ${graal_bin})" diff --git a/.github/workflows/publish b/.github/workflows/publish new file mode 100755 index 000000000..ca905b4c7 --- /dev/null +++ b/.github/workflows/publish @@ -0,0 +1,16 @@ +#!/bin/bash +# +# inputs: path(s) to file to release +# +auth="Authorization: token ${GITHUB_TOKEN}" +content="Content-Type:application/octet-stream" +release="$(python -c "import json; print(json.load(open('${GITHUB_EVENT_PATH}', 'r'))['release']['id'])")" + +# Upload every file +for file in "$@" +do + url="https://uploads.github.com/repos/${GITHUB_REPOSITORY}/releases/${release}/assets?name=$( basename "${file}" )" + echo "Publishing: ${file} to ${url}" + curl -sSL -XPOST -H "${auth}" --upload-file "${file}" --header "${content}" "${url}" || exit $? +done +exit 0 diff --git a/compiler/release b/compiler/release index d87a1a893..47741e120 100755 --- a/compiler/release +++ b/compiler/release @@ -17,7 +17,7 @@ then echo "release: environment variable GRAALVM_JAVA_HOME is not set" 1>&2 exit 1 fi - +export PATH="$GRAALVM_JAVA_HOME/bin:${PATH}" directory=`dirname $0` native_bin="$directory/native-fpp-`uname`-`uname -m`" native_image="$GRAALVM_JAVA_HOME/bin/native-image" From d126c6045675937330ba887baf6e453226a51309 Mon Sep 17 00:00:00 2001 From: Michael D Starch Date: Thu, 2 Jun 2022 19:04:44 -0700 Subject: [PATCH 03/13] adding initial python package wrapper and actions --- .github/actions/pypi/action.yml | 29 ++++ .github/workflows/build-native.yml | 19 ++- .gitignore | 5 + compiler/release | 8 +- fprime_fpp/__init__.py | 0 fprime_fpp/fprime_fpp_install.py | 256 +++++++++++++++++++++++++++++ setup.py | 113 +++++++++++++ 7 files changed, 427 insertions(+), 3 deletions(-) create mode 100644 .github/actions/pypi/action.yml create mode 100644 fprime_fpp/__init__.py create mode 100644 fprime_fpp/fprime_fpp_install.py create mode 100644 setup.py diff --git a/.github/actions/pypi/action.yml b/.github/actions/pypi/action.yml new file mode 100644 index 000000000..b1e7e7310 --- /dev/null +++ b/.github/actions/pypi/action.yml @@ -0,0 +1,29 @@ +name: 'Publish PYPI Package' +description: 'Publish an F´ PYPI Package' +inputs: + location: + required: true + default: $GITHUB_WORKSPACE + steps: + required: true + default: "sdist bdist_wheel" + repo: + required: true + default: "testpypi" +runs: + using: "composite" + steps: + - run: pip3 install -U pip wheel setuptools setuptools_scm twine + shell: bash + - run: | + cd ${{ inputs.location }} + python3 setup.py ${{ inputs.steps }} + shell: bash + - run: | + cd ${{ inputs.location }} + twine check dist/* + shell: bash + - run: | + cd ${{ inputs.location }} + twine upload -r ${{ inputs.repo }} -u "__token__" dist/* + shell: bash diff --git a/.github/workflows/build-native.yml b/.github/workflows/build-native.yml index 8c799ad77..7df11b99c 100644 --- a/.github/workflows/build-native.yml +++ b/.github/workflows/build-native.yml @@ -6,10 +6,10 @@ jobs: Build-Native-Images: strategy: matrix: - os: ["ubuntu-latest", "macos-latest"] + os: ["ubuntu-latest", "macos-10.15"] runs-on: ${{ matrix.os }} steps: - - name: "Checkout F´ Repository" + - name: "Checkout FPP Repository" uses: actions/checkout@v2 with: fetch-depth: 0 @@ -22,3 +22,18 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: ${GITHUB_WORKSPACE}/.github/workflows/publish native-fpp-*.tar.gz + Build-PyPI-Package: + needs: Build-Native-Images + runs-on: ubuntu-latest + steps: + - name: "Checkout FPP Repository" + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Test PyPI + uses: ./.github/actions/pypi + env: + TWINE_PASSWORD: ${{ secrets.TESTPYPI_CREDENTIAL }} + with: + steps: "sdist" + diff --git a/.gitignore b/.gitignore index d04de18f8..42cb66564 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ .* *.bak *~ +*.egg-info +__SHADOW__ +__pycache__ +dist/ +fprim_fpp/fpp_version_info.py diff --git a/compiler/release b/compiler/release index 47741e120..5b9e32bbf 100755 --- a/compiler/release +++ b/compiler/release @@ -27,6 +27,7 @@ mkdir -p "$native_bin" # Install jar files here $directory/install +cc --version # Convert jar files to binaries for jar_file in $directory/bin/*.jar do @@ -38,6 +39,11 @@ do echo "release: Failed to build $out_file" exit 1 fi + if ! $out_file --help + then + echo "release: Failed, $out_file not executable" + exit 1 + fi done # Clean up native directory @@ -47,4 +53,4 @@ rm "$native_bin"/*.txt release_tgz="$native_bin.tar.gz" tar -czf "$release_tgz" "$native_bin" -echo "Release archive written to $release_tgz" +echo "Release archive written to $release_tgz with size `du -hs $release_tgz`" diff --git a/fprime_fpp/__init__.py b/fprime_fpp/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/fprime_fpp/fprime_fpp_install.py b/fprime_fpp/fprime_fpp_install.py new file mode 100644 index 000000000..162f311ad --- /dev/null +++ b/fprime_fpp/fprime_fpp_install.py @@ -0,0 +1,256 @@ +""" install.py: + +Installs the FPP tool suite based on the version of this installer package. It will use FPP_DOWNLOAD_CACHE environment +variable to pull up previously downloaded items for users that wish to install offline. +""" +import atexit +import os +import platform +import shutil +import subprocess +import sys +import tarfile +import tempfile +import time +import urllib.request +import urllib.error + +from pathlib import Path +from typing import Iterable +from contextlib import contextmanager + + +def clean_at_exit(file_or_directory): + """Register file or for cleanup at exit + + In order to be a nice citizen and ensure that the system does not break when rerunning pieces of the system this + will clean-up files when the process is exiting. + """ + + def clean(path): + """Clean up the path""" + print(f"-- INFO -- Removing: {path}") + if os.path.isfile(path): + os.remove(path) + else: + shutil.rmtree(path, ignore_errors=True) + + atexit.register(clean, file_or_directory) + + +def setup_version(): + """Setup the version information for the fpp tools that will be installed + + There are two cases that we care about with respect to the version: + + 1. Building locally (e.g. pip install ., or python3 setup.py sdist): read version from the scm (e.g. git). + 2. Installing from existing package: read version from existing version file + """ + try: + from .fpp_version_info import FPP_TOOLS_VERSION + except ImportError: + process = subprocess.run( + ["git", "describe", "--tag", "--always"], stdout=subprocess.PIPE, text=True + ) + if process.returncode != 0: + print( + f"-- ERROR -- Cannot build locally without git and git repository", + file=sys.stderr, + ) + sys.exit(1) + scm_version = process.stdout.strip() + + # Write-version file for package completeness + version_path = Path(__file__).parent / "fpp_version_info.py" + clean_at_exit(version_path) + with open(version_path, "w") as file_handle: + file_handle.write(f'FPP_TOOLS_VERSION="{scm_version}"\n') + # Now the import should work as expected + from .fpp_version_info import FPP_TOOLS_VERSION + return FPP_TOOLS_VERSION + + +__FPP_TOOLS_VERSION__ = setup_version() +WORKING_DIR = Path(tempfile.gettempdir()) / "__FPP_WORKING_DIR__" +FPP_ARTIFACT_PREFIX = "native-fpp" +FPP_COMPRESSION_EXT = ".tar.gz" +GITHUB_URL = "https://github.com/LeStarch/fpp" +GITHUB_RELEASE_URL = "{GITHUB_URL}/releases/download/{version}/{artifact_string}" +SBT_URL = "https://github.com/sbt/sbt/releases/download/v1.6.2/sbt-1.6.2.tgz" + + +@contextmanager +def safe_chdir(path): + """Safely change directory, returning when done.""" + origin = os.getcwd() + try: + os.chdir(path) + yield + finally: + os.chdir(origin) + + +def get_artifact_string(version: str) -> str: + """Gets the platform string for the package. e.g. Darwin-x86_64""" + return f"{ FPP_ARTIFACT_PREFIX }-{ platform.system() }-{ platform.machine() }{ FPP_COMPRESSION_EXT }" + + +def wget(url: str): + """wget functionality to fetch a URL""" + print(f"-- INFO -- Fetching FPP tools at { url }", file=sys.stderr) + try: + urllib.request.urlretrieve(url, Path(url).name) + except urllib.error.HTTPError as error: + print( + f"-- WARN -- Failed to retrieve { url } with error: { error }", + file=sys.stderr, + ) + raise + + +def github_release_download(version: str): + """Attempts to get FPP via the FPP release""" + + # Three download tries + for _ in range(0, 3): + try: + release_url = GITHUB_RELEASE_URL.format( + GITHUB_URL=GITHUB_URL, + version=version, + artifact_string=get_artifact_string(version), + ) + wget(release_url) + except urllib.error.HTTPError as error: + retry_likely = "404" not in str(error) + # Check if this is a real error or not available error + if not retry_likely: + raise + print(f"-- INFO -- Retrying download to resolve: {error}") + time.sleep(5) # Throttle + + +def prepare_cache_dir(version: str) -> Path: + """Prepare the cache directory for the installation + + Detects a tar file of an expected version, and extracts the files from within it. This mimics the installation but + from published artifacts rather than an install from source. This will delete the tar file to ensure it does not + pollute the results. + + Args: + version: version string of expected artifacts + """ + expected_artifact = Path(os.getcwd()) / get_artifact_string(version) + # Extract files from tar without any paths + with tarfile.open(expected_artifact) as archive: + for member in archive.getmembers(): + if member.isreg(): + member.name = os.path.basename(member.name) + archive.extract(member, Path(os.getcwd())) + os.remove(expected_artifact) + + +def verify_download(version: str): + """Verify the download + + The downloaded products should be executable, and should produce a message with the --help flag that contains the + expected version. This will verify that that happened properly. + + Args: + version: version string of expected artifacts + """ + for potential in Path(os.getcwd()).iterdir(): + process = subprocess.run( + [str(potential), "--help"], stdout=subprocess.PIPE, text=True + ) + process.check_returncode() + if version not in process.stdout: + raise Exception(f"Download not of expected version: {version}") + + +def install_fpp(working_dir: Path) -> Path: + """Installs FPP of the specified version""" + version = __FPP_TOOLS_VERSION__ + + # Put everything in the current working directory + with safe_chdir(working_dir): + try: + github_release_download(version) + prepare_cache_dir(version) + verify_download(version) + except urllib.error.HTTPError: + install_fpp_via_git(working_dir) + except OSError as ose: + print(f"-- ERROR -- Failed find expected download: {ose}", file=sys.stderr) + sys.exit(-1) + except Exception as exc: + print(f"-- ERROR -- Failed to install tools: {exc}", file=sys.stderr) + (working_dir / version).touch() + return working_dir + + +def install_fpp_via_git(installation_directory: Path): + """Installs FPP from git + + Should FPP not be available as a published version, this will clone the FPP repo, checkout, and build the FPP tools + for the given version. This requires the following tools to exist on the system: git, sh, java, and sbt. These tools + will be checked and then the process will run and intall into the specified directory. + + Args: + installation_directory: directory to install into + """ + tools = ["sh", "java"] + for tool in tools: + if not shutil.which(tool): + print( + f"-- ERROR -- {tool} must exist on PATH to build from source", + file=sys.stderr, + ) + sys.exit(-1) + with tempfile.TemporaryDirectory() as tools_directory: + os.chdir(tools_directory) + wget(SBT_URL) + with tarfile.open(os.path.basename(SBT_URL)) as archive: + archive.extractall(".") + sbt_path = Path(tools_directory) / "sbt" / "bin" + subprocess_environment = os.environ.copy() + subprocess_environment["PATH"] = f"{ sbt_path }:{ os.environ.get('PATH') }" + steps = [ + [ + os.path.join(os.path.dirname(__file__), "..", "compiler", "install"), + str(installation_directory), + ], + ] + for step in steps: + print(f"-- INFO -- Running { ' '.join(step) }") + completed = subprocess.run(step, env=subprocess_environment) + if completed.returncode != 0: + print(f"-- ERROR -- Failed to run { ' '.join(step) }", file=sys.stderr) + sys.exit(-1) + + return installation_directory + + +def iterate_fpp_tools(working_dir: Path) -> Iterable[Path]: + """Iterates through FPP tools""" + executables = [ + os.access(executable, os.X_OK) for executable in working_dir.iterdir() + ] + # Check if executables exist and the version file was touched + if executables and (working_dir / __FPP_TOOLS_VERSION__).exists(): + return working_dir.iterdir() + # Clean up before a possible re-installation + shutil.rmtree(working_dir) + working_dir.mkdir() + return install_fpp(working_dir).iterdir() + + +@contextmanager +def clean_install_fpp(): + """Cleanly installs FPP in subdirectory, cleaning when finished""" + WORKING_DIR.mkdir(exist_ok=True) + + def lazy_loader(): + """Prevents the download of FPP items until actually enumerated""" + yield from iterate_fpp_tools(WORKING_DIR) + + yield lazy_loader() diff --git a/setup.py b/setup.py new file mode 100644 index 000000000..de299fb0f --- /dev/null +++ b/setup.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python +#### +# fprime-fpp: +# +# This package is used as a simple installer for the FPP tool suite used alongside fprime. The intent is to allow users to +# install the FPP tools in the same manner that all of the other tools (python) are installed. It also give an easy way +# for the "fprime" package to specify a specific dependency on FPP tools versions +#### +import shutil +from pathlib import Path +from setuptools import setup +from setuptools.command.sdist import sdist +from setuptools.command.install import install + +from fprime_fpp.fprime_fpp_install import clean_install_fpp, clean_at_exit, WORKING_DIR + + +SHADOW_DIR = Path("__SHADOW__") +SHADOW_CACHE = None + + +def make_shadows(shadow_dir, executables): + """Makes shadow copies of the fpp executables + + The PIP sdist package must be aware of the names and paths of installed FPP executables, even though they are + downloaded during the install phase (on client). Thus this function when run with touch_only creates 0-size file + "shadows" of the files for the distributions. + + When run with touch_only=False, this function realizes those shadows from the downloaded copy. This is done on + install to force the files to be real executables. + """ + SHADOW_DIR.mkdir(exist_ok=True) + for executable in executables: + shadow_path = shadow_dir / executable.name + yield shadow_path, executable + + +with clean_install_fpp() as lazy_executables: + + def cache_shadows(name_only=False): + """Caches the output of the shadow generator + + Since the shadow data is used multiple times and incurs a very expensive call to the Github download/cloning code, + the output is cached here and subsequent calls for this data return the cached version. However, this function + maintains the lazy nature of executables and make_shadows preventing early execution of the expensive calls. + """ + global SHADOW_CACHE + iterable = ( + SHADOW_CACHE + if SHADOW_CACHE is not None + else make_shadows(SHADOW_DIR, lazy_executables) + ) + + new_cache = [] + for shadow_pair in iterable: + new_cache.append(shadow_pair) + yield str(shadow_pair[0]) if name_only else shadow_pair + SHADOW_CACHE = new_cache + + class FppSdist(sdist): + """Command to run at 'sdist' stage + + During the 'sdist' stage we want to download an expected FPP tarball and understand what is included inside + (in terms of files). Then create a shadow set of 0-byte files as placeholders in the distribution. The install + stage (below) will handle the work of replacing the shadow placeholders with the actual platform-specific + binaries. + + 'sdist' is also the only case where the environment variable is required to be set for building these tools. In + other cases it will be set by fprime and will default to package when not set. This here we check for that + variable and error if not set. + """ + + def run(self): + """sdist package run implementation""" + for shadow, _ in cache_shadows(): + shadow.touch() + sdist.run(self) + + class FppInstall(install): + def run(self): + """install scripts for giles""" + clean_at_exit(WORKING_DIR) + for shadow, executable in cache_shadows(): + shutil.copy(executable, shadow) + install.run(self) + + setup( + name="fprime-fpp", + use_scm_version={"root": ".", "relative_to": __file__}, + license="Apache 2.0 License", + description="FPP distribution package", + long_description=""" + Package used to deploy the FPP tool suite in the same manner as all other tools. FPP will be installed in the user's + virtual environment or python distribution alongside other tools being user (e.g. fprime-util). + """, + url="https://github.com/nasa/fprime", + keywords=["fpp", "fprime", "embedded", "nasa"], + project_urls={"Issue Tracker": "https://github.com/nasa/fprime/issues"}, + author="Michael Starch", + author_email="Michael.D.Starch@jpl.nasa.gov", + classifiers=[ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Operating System :: MacOS", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + ], + python_requires=">=3.6", + data_files=[("bin", cache_shadows(name_only=True))], + packages=["fprime_fpp"], + cmdclass={"sdist": FppSdist, "install": FppInstall}, + ) From 0bdd286bdd7179593786791a3485994ab67a2535 Mon Sep 17 00:00:00 2001 From: Michael D Starch Date: Mon, 13 Jun 2022 19:26:53 -0700 Subject: [PATCH 04/13] lestarch: adding in installation test for repository --- .github/actions/pypi/action.yml | 12 ++----- .github/actions/pypi/package | 53 ++++++++++++++++++++++++++++++ .github/workflows/build-native.yml | 10 +++++- 3 files changed, 65 insertions(+), 10 deletions(-) create mode 100755 .github/actions/pypi/package diff --git a/.github/actions/pypi/action.yml b/.github/actions/pypi/action.yml index b1e7e7310..7884a143d 100644 --- a/.github/actions/pypi/action.yml +++ b/.github/actions/pypi/action.yml @@ -1,6 +1,8 @@ name: 'Publish PYPI Package' description: 'Publish an F´ PYPI Package' inputs: + package: + required: true location: required: true default: $GITHUB_WORKSPACE @@ -17,13 +19,5 @@ runs: shell: bash - run: | cd ${{ inputs.location }} - python3 setup.py ${{ inputs.steps }} - shell: bash - - run: | - cd ${{ inputs.location }} - twine check dist/* - shell: bash - - run: | - cd ${{ inputs.location }} - twine upload -r ${{ inputs.repo }} -u "__token__" dist/* + ${{ inputs.location }}/.github/actions/pypi/package "${{ inputs.package }}" "${{ inputs.repo }}" ${{ inputs.steps }} shell: bash diff --git a/.github/actions/pypi/package b/.github/actions/pypi/package new file mode 100755 index 000000000..b744fac0d --- /dev/null +++ b/.github/actions/pypi/package @@ -0,0 +1,53 @@ +#!/bin/bash +if (( $# < 3 )) +then + echo "[ERROR] Must specify a package name, repo, and at least one build step" + exit 1 +fi + +# Read package from first argument +package="$1" + +# Read repo from second argument +repo="$2" +shift +shift + +# Remaining arguments are build steps +python3 setup.py "$@" +if (( $? != 0 )) +then + echo "[ERROR] Failed to build package with: $@" + exit 1 +fi + +# Check the repo before uploading +twine check dist/* +if (( $? != 0 )) +then + echo "[ERROR] Failed to check package" + exit 2 +fi + +# Upload the package to the given repo +twine upload -r "${repo}" -u "__token__" dist/* +if (( $? != 0 )) +then + echo "[ERROR] Failed to upload package to: ${repo}" + exit 3 +fi + +# Prepare for installation of specific package version +version="$(git describe --tag --always)" +args="" +if [[ "${repo}" == "testpypi" ]] +then + args="--index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/" +fi + +pip install ${args} "${package}==${version}" +if (( $? != 0 )) +then + echo "[ERROR] Failed to install package with: ${args} ${package}==${version}" + exit 4 +fi diff --git a/.github/workflows/build-native.yml b/.github/workflows/build-native.yml index 7df11b99c..348070e07 100644 --- a/.github/workflows/build-native.yml +++ b/.github/workflows/build-native.yml @@ -35,5 +35,13 @@ jobs: env: TWINE_PASSWORD: ${{ secrets.TESTPYPI_CREDENTIAL }} with: + package: "fprime-fpp" + steps: "sdist" + - name: PyPI + uses: ./.github/actions/pypi + env: + TWINE_PASSWORD: ${{ secrets.PYPI_CREDENTIAL }} + with: + repo: "pypi" + package: "fprime-fpp" steps: "sdist" - From 91b0f30e1948d73141b5d4d147a0a8ecd3dbdf3d Mon Sep 17 00:00:00 2001 From: Michael D Starch Date: Tue, 14 Jun 2022 09:44:55 -0700 Subject: [PATCH 05/13] lestarch: adding retry logic to the PyPI test install --- .github/actions/pypi/package | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/actions/pypi/package b/.github/actions/pypi/package index b744fac0d..fa6bc7821 100755 --- a/.github/actions/pypi/package +++ b/.github/actions/pypi/package @@ -45,9 +45,15 @@ then args="--index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/" fi -pip install ${args} "${package}==${version}" -if (( $? != 0 )) -then - echo "[ERROR] Failed to install package with: ${args} ${package}==${version}" - exit 4 -fi +# Retry logic as PyPI requires a bit to catch up +for retry in 1 2 3 +do + pip install ${args} "${package}==${version}" + if (( $? == 0 )) + then + exit 0 + fi + echo "[ERROR] Failed to install package with: ${args} ${package}==${version}. Retrying." + sleep 30 +done +exit 4 From 2531a8da50af3fd3642fb75ffe8baafc7d853c04 Mon Sep 17 00:00:00 2001 From: Michael D Starch Date: Tue, 14 Jun 2022 12:49:25 -0700 Subject: [PATCH 06/13] lestarch: switching to common action for publishing --- .github/workflows/build-native.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-native.yml b/.github/workflows/build-native.yml index 348070e07..cd3774188 100644 --- a/.github/workflows/build-native.yml +++ b/.github/workflows/build-native.yml @@ -31,14 +31,14 @@ jobs: with: fetch-depth: 0 - name: Test PyPI - uses: ./.github/actions/pypi + uses: fprime-community/publish-pypi@main env: TWINE_PASSWORD: ${{ secrets.TESTPYPI_CREDENTIAL }} with: package: "fprime-fpp" steps: "sdist" - name: PyPI - uses: ./.github/actions/pypi + uses: fprime-community/publish-pypi@main env: TWINE_PASSWORD: ${{ secrets.PYPI_CREDENTIAL }} with: From 1d2f8d702244318d34739eedb375236a33e001aa Mon Sep 17 00:00:00 2001 From: Michael D Starch Date: Tue, 14 Jun 2022 14:27:20 -0700 Subject: [PATCH 07/13] lestarch: cleaning up image --- .github/actions/pypi/action.yml | 23 ------------- .github/actions/pypi/package | 59 --------------------------------- compiler/release | 4 ++- 3 files changed, 3 insertions(+), 83 deletions(-) delete mode 100644 .github/actions/pypi/action.yml delete mode 100755 .github/actions/pypi/package diff --git a/.github/actions/pypi/action.yml b/.github/actions/pypi/action.yml deleted file mode 100644 index 7884a143d..000000000 --- a/.github/actions/pypi/action.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: 'Publish PYPI Package' -description: 'Publish an F´ PYPI Package' -inputs: - package: - required: true - location: - required: true - default: $GITHUB_WORKSPACE - steps: - required: true - default: "sdist bdist_wheel" - repo: - required: true - default: "testpypi" -runs: - using: "composite" - steps: - - run: pip3 install -U pip wheel setuptools setuptools_scm twine - shell: bash - - run: | - cd ${{ inputs.location }} - ${{ inputs.location }}/.github/actions/pypi/package "${{ inputs.package }}" "${{ inputs.repo }}" ${{ inputs.steps }} - shell: bash diff --git a/.github/actions/pypi/package b/.github/actions/pypi/package deleted file mode 100755 index fa6bc7821..000000000 --- a/.github/actions/pypi/package +++ /dev/null @@ -1,59 +0,0 @@ -#!/bin/bash -if (( $# < 3 )) -then - echo "[ERROR] Must specify a package name, repo, and at least one build step" - exit 1 -fi - -# Read package from first argument -package="$1" - -# Read repo from second argument -repo="$2" -shift -shift - -# Remaining arguments are build steps -python3 setup.py "$@" -if (( $? != 0 )) -then - echo "[ERROR] Failed to build package with: $@" - exit 1 -fi - -# Check the repo before uploading -twine check dist/* -if (( $? != 0 )) -then - echo "[ERROR] Failed to check package" - exit 2 -fi - -# Upload the package to the given repo -twine upload -r "${repo}" -u "__token__" dist/* -if (( $? != 0 )) -then - echo "[ERROR] Failed to upload package to: ${repo}" - exit 3 -fi - -# Prepare for installation of specific package version -version="$(git describe --tag --always)" -args="" -if [[ "${repo}" == "testpypi" ]] -then - args="--index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/" -fi - -# Retry logic as PyPI requires a bit to catch up -for retry in 1 2 3 -do - pip install ${args} "${package}==${version}" - if (( $? == 0 )) - then - exit 0 - fi - echo "[ERROR] Failed to install package with: ${args} ${package}==${version}. Retrying." - sleep 30 -done -exit 4 diff --git a/compiler/release b/compiler/release index 5b9e32bbf..7793b4ec8 100755 --- a/compiler/release +++ b/compiler/release @@ -52,5 +52,7 @@ rm "$native_bin"/*.txt # Create tar ball release_tgz="$native_bin.tar.gz" tar -czf "$release_tgz" "$native_bin" - +sync +sync +sync echo "Release archive written to $release_tgz with size `du -hs $release_tgz`" From 9f46acaea818e6c176dcef8f81bd3b766780a78a Mon Sep 17 00:00:00 2001 From: Michael D Starch Date: Tue, 14 Jun 2022 16:10:47 -0700 Subject: [PATCH 08/13] lestarch: updating release to run install-trace and test --- .github/workflows/build-native.yml | 3 ++- .github/workflows/env-setup | 8 +++---- compiler/release | 38 ++++++++++++++++++++++++------ 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build-native.yml b/.github/workflows/build-native.yml index cd3774188..50829f273 100644 --- a/.github/workflows/build-native.yml +++ b/.github/workflows/build-native.yml @@ -16,7 +16,8 @@ jobs: - name: Image Build Step working-directory: ./compiler run: | - GRAALVM_JAVA_HOME="$( ${GITHUB_WORKSPACE}/.github/workflows/env-setup )" ./release + . ${GITHUB_WORKSPACE}/.github/workflows/env-setup + ./release mv native-fpp-*.tar.gz "${GITHUB_WORKSPACE}" - name: Publish Release Binaries env: diff --git a/.github/workflows/env-setup b/.github/workflows/env-setup index 6eed9442d..80fc2cd9b 100755 --- a/.github/workflows/env-setup +++ b/.github/workflows/env-setup @@ -2,7 +2,7 @@ graal_ver="22.1.0" graal_os="$( uname -s | tr "[:upper:]" "[:lower:]")" graal_ar="graalvm-ce-java11-${graal_os}-amd64-${graal_ver}.tar.gz" -graal_dir="graalvm-ce-java11-${graal_ver}" +graal_dir="$(pwd)/graalvm-ce-java11-${graal_ver}" graal_url="https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-${graal_ver}/${graal_ar}" # Download Graal @@ -12,8 +12,8 @@ if [ ! -d "${graal_bin}" ] then graal_bin="${graal_dir}/Contents/Home/bin" fi +export PATH="${graal_bin}:${PATH}" +export GRAALVM_JAVA_HOME="$(dirname ${graal_bin})" # Install native image -${graal_bin}/gu install native-image 1>&2 +${graal_bin}/gu install native-image -# Return Graal home directory -echo "$(dirname ${graal_bin})" diff --git a/compiler/release b/compiler/release index 7793b4ec8..1fcc07746 100755 --- a/compiler/release +++ b/compiler/release @@ -17,17 +17,43 @@ then echo "release: environment variable GRAALVM_JAVA_HOME is not set" 1>&2 exit 1 fi -export PATH="$GRAALVM_JAVA_HOME/bin:${PATH}" directory=`dirname $0` native_bin="$directory/native-fpp-`uname`-`uname -m`" native_image="$GRAALVM_JAVA_HOME/bin/native-image" +meta_inf_dir="$directory/lib/src/main/resources/META-INF/native-image/" +# Clean-up META-INF +rm -rf "$meta_inf_dir" +# Make directories +mkdir -p "$meta_inf_dir" mkdir -p "$native_bin" +# Versioning information +echo "C compiler version" +cc --version +echo "Java version" +java --version +echo "Native Image Version" +native-image --version + + # Install jar files here -$directory/install +$directory/install-trace +# Test a run before running test +$directory/bin/fpp-locate-defs tools/fpp-locate-defs/test/defs/defs-1.fpp tools/fpp-locate-defs/test/defs/defs-2.fpp 1>/dev/null +if [ $? -ne 0 ] +then + echo "[ERROR] Failed to run tracable JAR" + exit 1 +fi + +# Trace through testing +$directory/test +if [ $? -ne 0 ] +then + echo "[WARNING] Failed to run tests" +fi -cc --version # Convert jar files to binaries for jar_file in $directory/bin/*.jar do @@ -39,7 +65,7 @@ do echo "release: Failed to build $out_file" exit 1 fi - if ! $out_file --help + if ! $out_file --help 1>/dev/null then echo "release: Failed, $out_file not executable" exit 1 @@ -52,7 +78,5 @@ rm "$native_bin"/*.txt # Create tar ball release_tgz="$native_bin.tar.gz" tar -czf "$release_tgz" "$native_bin" -sync -sync -sync +sync; sync; sync; echo "Release archive written to $release_tgz with size `du -hs $release_tgz`" From 21d41afddc47c6984484759accf80a84638cb9b4 Mon Sep 17 00:00:00 2001 From: Michael D Starch Date: Wed, 15 Jun 2022 14:58:14 -0700 Subject: [PATCH 09/13] adding in syncs to ensure files are on-disk --- compiler/release | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/release b/compiler/release index 1fcc07746..d3d0a74e7 100755 --- a/compiler/release +++ b/compiler/release @@ -21,8 +21,6 @@ directory=`dirname $0` native_bin="$directory/native-fpp-`uname`-`uname -m`" native_image="$GRAALVM_JAVA_HOME/bin/native-image" meta_inf_dir="$directory/lib/src/main/resources/META-INF/native-image/" -# Clean-up META-INF -rm -rf "$meta_inf_dir" # Make directories mkdir -p "$meta_inf_dir" @@ -53,7 +51,7 @@ if [ $? -ne 0 ] then echo "[WARNING] Failed to run tests" fi - +sync; sync; sync; # Convert jar files to binaries for jar_file in $directory/bin/*.jar do @@ -65,15 +63,18 @@ do echo "release: Failed to build $out_file" exit 1 fi + sync; sync; sync; if ! $out_file --help 1>/dev/null then echo "release: Failed, $out_file not executable" exit 1 fi done +sync; sync; sync; # Clean up native directory rm "$native_bin"/*.txt +sync; sync; sync; # Create tar ball release_tgz="$native_bin.tar.gz" From 306dc7185dc5fa0b12540b6acf1c3fced6bbedb5 Mon Sep 17 00:00:00 2001 From: Michael D Starch Date: Wed, 15 Jun 2022 19:25:01 -0700 Subject: [PATCH 10/13] lestarch: latest native image configuration --- .../META-INF/native-image/jni-config.json | 66 ++++++++++++++++++- .../predefined-classes-config.json | 8 +++ .../META-INF/native-image/reflect-config.json | 34 ++++++---- .../native-image/resource-config.json | 5 +- .../native-image/serialization-config.json | 8 ++- 5 files changed, 106 insertions(+), 15 deletions(-) create mode 100644 compiler/lib/src/main/resources/META-INF/native-image/predefined-classes-config.json diff --git a/compiler/lib/src/main/resources/META-INF/native-image/jni-config.json b/compiler/lib/src/main/resources/META-INF/native-image/jni-config.json index 7d5fedc5b..d7b26c935 100644 --- a/compiler/lib/src/main/resources/META-INF/native-image/jni-config.json +++ b/compiler/lib/src/main/resources/META-INF/native-image/jni-config.json @@ -1,9 +1,73 @@ [ +{ + "name":"fpp.compiler.FPPFormat", + "methods":[{"name":"main","parameterTypes":["java.lang.String[]"] }] +}, +{ + "name":"fpp.compiler.FPPSyntax", + "methods":[{"name":"main","parameterTypes":["java.lang.String[]"] }] +}, +{ + "name":"fpp.compiler.tools.FPPCheck", + "methods":[{"name":"main","parameterTypes":["java.lang.String[]"] }] +}, +{ + "name":"fpp.compiler.tools.FPPDepend", + "methods":[{"name":"main","parameterTypes":["java.lang.String[]"] }] +}, +{ + "name":"fpp.compiler.tools.FPPFilenames", + "methods":[{"name":"main","parameterTypes":["java.lang.String[]"] }] +}, +{ + "name":"fpp.compiler.tools.FPPFromXml", + "methods":[{"name":"main","parameterTypes":["java.lang.String[]"] }] +}, +{ + "name":"fpp.compiler.tools.FPPLocateDefs", + "methods":[{"name":"main","parameterTypes":["java.lang.String[]"] }] +}, +{ + "name":"fpp.compiler.tools.FPPLocateUses", + "methods":[{"name":"main","parameterTypes":["java.lang.String[]"] }] +}, +{ + "name":"fpp.compiler.tools.FPPToCpp", + "methods":[{"name":"main","parameterTypes":["java.lang.String[]"] }] +}, +{ + "name":"fpp.compiler.tools.FPPToXml", + "methods":[{"name":"main","parameterTypes":["java.lang.String[]"] }] +}, { "name":"java.lang.ClassLoader", - "methods":[{"name":"getPlatformClassLoader","parameterTypes":[] }] + "methods":[ + {"name":"getPlatformClassLoader","parameterTypes":[] }, + {"name":"loadClass","parameterTypes":["java.lang.String"] } + ] }, { "name":"java.lang.NoSuchMethodError" +}, +{ + "name":"java.lang.String", + "methods":[ + {"name":"lastIndexOf","parameterTypes":["int"] }, + {"name":"substring","parameterTypes":["int"] } + ] +}, +{ + "name":"java.lang.System", + "methods":[ + {"name":"getProperty","parameterTypes":["java.lang.String"] }, + {"name":"setProperty","parameterTypes":["java.lang.String","java.lang.String"] } + ] +}, +{ + "name":"jdk.internal.loader.ClassLoaders$PlatformClassLoader" +}, +{ + "name":"org.graalvm.jniutils.JNIExceptionWrapperEntryPoints", + "methods":[{"name":"getClassName","parameterTypes":["java.lang.Class"] }] } ] diff --git a/compiler/lib/src/main/resources/META-INF/native-image/predefined-classes-config.json b/compiler/lib/src/main/resources/META-INF/native-image/predefined-classes-config.json new file mode 100644 index 000000000..0e79b2c5d --- /dev/null +++ b/compiler/lib/src/main/resources/META-INF/native-image/predefined-classes-config.json @@ -0,0 +1,8 @@ +[ + { + "type":"agent-extracted", + "classes":[ + ] + } +] + diff --git a/compiler/lib/src/main/resources/META-INF/native-image/reflect-config.json b/compiler/lib/src/main/resources/META-INF/native-image/reflect-config.json index 6404a8b36..0504e028e 100644 --- a/compiler/lib/src/main/resources/META-INF/native-image/reflect-config.json +++ b/compiler/lib/src/main/resources/META-INF/native-image/reflect-config.json @@ -1,4 +1,16 @@ [ +{ + "name":"[Lfpp.compiler.analysis.ComponentInstance;" +}, +{ + "name":"[Lfpp.compiler.analysis.Connection;" +}, +{ + "name":"[Lfpp.compiler.codegen.Line;" +}, +{ + "name":"[Ljava.lang.String;" +}, { "name":"com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl", "methods":[{"name":"","parameterTypes":[] }] @@ -11,30 +23,30 @@ }, { "name":"fpp.compiler.analysis.Format$Parser$", - "fields":[{"name":"0bitmap$1", "allowUnsafeAccess":true}] + "fields":[{"name":"0bitmap$1"}] }, { "name":"fpp.compiler.codegen.CppDocCppWriter$", - "fields":[{"name":"0bitmap$1", "allowUnsafeAccess":true}] + "fields":[{"name":"0bitmap$1"}] }, { "name":"fpp.compiler.codegen.CppDocHppWriter$", - "fields":[{"name":"0bitmap$1", "allowUnsafeAccess":true}] + "fields":[{"name":"0bitmap$1"}] }, { "name":"fpp.compiler.codegen.Line[]" }, { "name":"fpp.compiler.syntax.Lexer$", - "fields":[{"name":"0bitmap$2", "allowUnsafeAccess":true}] + "fields":[{"name":"0bitmap$2"}] }, { "name":"fpp.compiler.syntax.Lexer$$anon$1", - "fields":[{"name":"0bitmap$1", "allowUnsafeAccess":true}] + "fields":[{"name":"0bitmap$1"}] }, { "name":"fpp.compiler.syntax.Parser$", - "fields":[{"name":"0bitmap$1", "allowUnsafeAccess":true}] + "fields":[{"name":"0bitmap$1"}] }, { "name":"java.lang.ClassValue" @@ -48,23 +60,23 @@ }, { "name":"scala.util.parsing.input.OffsetPosition", - "fields":[{"name":"0bitmap$1", "allowUnsafeAccess":true}] + "fields":[{"name":"0bitmap$1"}] }, { "name":"scala.util.parsing.input.OffsetPosition$", - "fields":[{"name":"0bitmap$2", "allowUnsafeAccess":true}] + "fields":[{"name":"0bitmap$2"}] }, { "name":"scala.xml.XML$", - "fields":[{"name":"0bitmap$2", "allowUnsafeAccess":true}] + "fields":[{"name":"0bitmap$2"}] }, { "name":"scala.xml.parsing.FactoryAdapter", - "fields":[{"name":"0bitmap$1", "allowUnsafeAccess":true}] + "fields":[{"name":"0bitmap$1"}] }, { "name":"scopt.OParser$", - "fields":[{"name":"0bitmap$1", "allowUnsafeAccess":true}] + "fields":[{"name":"0bitmap$1"}] }, { "name":"sun.misc.Unsafe", diff --git a/compiler/lib/src/main/resources/META-INF/native-image/resource-config.json b/compiler/lib/src/main/resources/META-INF/native-image/resource-config.json index 8d7333115..1ea7213de 100644 --- a/compiler/lib/src/main/resources/META-INF/native-image/resource-config.json +++ b/compiler/lib/src/main/resources/META-INF/native-image/resource-config.json @@ -1,5 +1,8 @@ { "resources":{ "includes":[]}, - "bundles":[{"name":"com.sun.org.apache.xerces.internal.impl.msg.XMLMessages"}] + "bundles":[{ + "name":"com.sun.org.apache.xerces.internal.impl.msg.XMLMessages", + "locales":["und"] + }] } diff --git a/compiler/lib/src/main/resources/META-INF/native-image/serialization-config.json b/compiler/lib/src/main/resources/META-INF/native-image/serialization-config.json index 0d4f101c7..bf554e062 100644 --- a/compiler/lib/src/main/resources/META-INF/native-image/serialization-config.json +++ b/compiler/lib/src/main/resources/META-INF/native-image/serialization-config.json @@ -1,2 +1,6 @@ -[ -] +{ + "types":[ + ], + "lambdaCapturingTypes":[ + ] +} From f9567ebf7616ad744a77297116d74bfb89dee161 Mon Sep 17 00:00:00 2001 From: Michael D Starch Date: Wed, 15 Jun 2022 19:29:08 -0700 Subject: [PATCH 11/13] lestarch: switching darwin to always use x86 --- fprime_fpp/fprime_fpp_install.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fprime_fpp/fprime_fpp_install.py b/fprime_fpp/fprime_fpp_install.py index 162f311ad..a1ab44368 100644 --- a/fprime_fpp/fprime_fpp_install.py +++ b/fprime_fpp/fprime_fpp_install.py @@ -92,7 +92,12 @@ def safe_chdir(path): def get_artifact_string(version: str) -> str: """Gets the platform string for the package. e.g. Darwin-x86_64""" - return f"{ FPP_ARTIFACT_PREFIX }-{ platform.system() }-{ platform.machine() }{ FPP_COMPRESSION_EXT }" + system = platform.system() + architecture = platform.machine() + # Always use x86 variants for Darwin + if system == "Darwin": + architecture = "x86_64" + return f"{ FPP_ARTIFACT_PREFIX }-{ system }-{ architecture }{ FPP_COMPRESSION_EXT }" def wget(url: str): From aa2ec8ec869d7d7be5a88061cd46a11b6be8a7eb Mon Sep 17 00:00:00 2001 From: Michael D Starch Date: Wed, 15 Jun 2022 22:55:46 -0700 Subject: [PATCH 12/13] lestarch: adding failure check when a botched image is too-small --- compiler/release | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/compiler/release b/compiler/release index d3d0a74e7..9ab68e039 100755 --- a/compiler/release +++ b/compiler/release @@ -80,4 +80,10 @@ sync; sync; sync; release_tgz="$native_bin.tar.gz" tar -czf "$release_tgz" "$native_bin" sync; sync; sync; +# Check if file size too small +if [ `stat -f %z $release_tgz` -lt 55846858 ] +then + echo "Release archive $release_tgz too small with size `du -hs $release_tgz`" + exit 99 +fi echo "Release archive written to $release_tgz with size `du -hs $release_tgz`" From 6ec0d4dd6442a3d48cb2dfc5c3f163872785bcd1 Mon Sep 17 00:00:00 2001 From: Michael D Starch Date: Thu, 16 Jun 2022 10:15:44 -0700 Subject: [PATCH 13/13] lestarch: final touches for PR --- fprime_fpp/fprime_fpp_install.py | 2 +- setup.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/fprime_fpp/fprime_fpp_install.py b/fprime_fpp/fprime_fpp_install.py index a1ab44368..9ea1dccf5 100644 --- a/fprime_fpp/fprime_fpp_install.py +++ b/fprime_fpp/fprime_fpp_install.py @@ -74,7 +74,7 @@ def setup_version(): WORKING_DIR = Path(tempfile.gettempdir()) / "__FPP_WORKING_DIR__" FPP_ARTIFACT_PREFIX = "native-fpp" FPP_COMPRESSION_EXT = ".tar.gz" -GITHUB_URL = "https://github.com/LeStarch/fpp" +GITHUB_URL = "https://github.com/fprime-community/fpp" GITHUB_RELEASE_URL = "{GITHUB_URL}/releases/download/{version}/{artifact_string}" SBT_URL = "https://github.com/sbt/sbt/releases/download/v1.6.2/sbt-1.6.2.tgz" diff --git a/setup.py b/setup.py index de299fb0f..fcc3a6cb6 100644 --- a/setup.py +++ b/setup.py @@ -106,7 +106,8 @@ def run(self): "Programming Language :: Python", "Programming Language :: Python :: 3", ], - python_requires=">=3.6", + setup_requires=["setuptools_scm"], + python_requires=">=3.7", data_files=[("bin", cache_shadows(name_only=True))], packages=["fprime_fpp"], cmdclass={"sdist": FppSdist, "install": FppInstall},