From dccb10bdcc01b878dd4c87633f3bdad76330fe4a Mon Sep 17 00:00:00 2001 From: Liam McSherry Date: Sun, 16 Jul 2023 15:25:38 +0000 Subject: [PATCH 1/3] Attempt to init library before adding it to `fusesoc.conf`. --- fusesoc/config.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/fusesoc/config.py b/fusesoc/config.py index 80c61740..6ad9f6bf 100644 --- a/fusesoc/config.py +++ b/fusesoc/config.py @@ -265,7 +265,6 @@ def add_library(self, library): from fusesoc.provider import get_provider section_name = "library." + library.name - if section_name in self._cp.sections(): logger.warning( "Not adding library. {} already exists in configuration file".format( @@ -274,6 +273,13 @@ def add_library(self, library): ) return + try: + provider = get_provider(library.sync_type) + except ImportError: + raise RuntimeError("Invalid sync-type '{}'".format(library["sync-type"])) + + provider.init_library(library) + self._cp.add_section(section_name) self._cp.set(section_name, "location", library.location) @@ -288,11 +294,4 @@ def add_library(self, library): _auto_sync = "true" if library.auto_sync else "false" self._cp.set(section_name, "auto-sync", _auto_sync) - try: - provider = get_provider(library.sync_type) - except ImportError: - raise RuntimeError("Invalid sync-type '{}'".format(library["sync-type"])) - - provider.init_library(library) - self.write() From 98cdb33f9fb9b902e918c6619609a7e55734572f Mon Sep 17 00:00:00 2001 From: Liam McSherry Date: Sun, 16 Jul 2023 16:13:04 +0000 Subject: [PATCH 2/3] Return `subprocess.Popen` from `Launcher.run()`. --- fusesoc/utils.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/fusesoc/utils.py b/fusesoc/utils.py index 7c7e20ea..8f79c3e5 100644 --- a/fusesoc/utils.py +++ b/fusesoc/utils.py @@ -6,6 +6,7 @@ import subprocess import sys import warnings +from typing import Any import yaml @@ -25,15 +26,20 @@ def __init__(self, cmd, args=[], cwd=None): self.args = args self.cwd = cwd - def run(self): + def run(self, **kwargs) -> subprocess.Popen: """Runs the cmd with args after converting them all to strings via str""" logger.debug(self.cwd or "./") logger.debug(" " + str(self)) + + cmd = map(str, [self.cmd] + self.args) try: - subprocess.check_call( - map(str, [self.cmd] + self.args), - cwd=self.cwd, - ), + proc = subprocess.Popen(args=cmd, cwd=self.cwd, **kwargs) + proc.wait() + + if proc.returncode != 0: + raise subprocess.CalledProcessError(proc.returncode, cmd) + + return proc except FileNotFoundError: raise RuntimeError( "Command '" + self.cmd + "' not found. Make sure it is in $PATH" From 3272eab30b86b9eeeee8614f86565546ac536ecb Mon Sep 17 00:00:00 2001 From: Liam McSherry Date: Sun, 16 Jul 2023 17:13:24 +0000 Subject: [PATCH 3/3] Add `env` provider. --- fusesoc/config.py | 5 ++- fusesoc/coremanager.py | 7 ++++ fusesoc/librarymanager.py | 6 ++-- fusesoc/main.py | 2 +- fusesoc/provider/env.py | 76 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 fusesoc/provider/env.py diff --git a/fusesoc/config.py b/fusesoc/config.py index 6ad9f6bf..60979521 100644 --- a/fusesoc/config.py +++ b/fusesoc/config.py @@ -282,7 +282,10 @@ def add_library(self, library): self._cp.add_section(section_name) - self._cp.set(section_name, "location", library.location) + # Libraries in special environmental folders may not have a fixed location, but + # if they aren't set up to auto-sync they should + if library.sync_type not in ["env"] or not library.auto_sync: + self._cp.set(section_name, "location", library.location) if library.sync_type: self._cp.set(section_name, "sync-uri", library.sync_uri) diff --git a/fusesoc/coremanager.py b/fusesoc/coremanager.py index 8b09848a..107efe51 100644 --- a/fusesoc/coremanager.py +++ b/fusesoc/coremanager.py @@ -221,7 +221,14 @@ def __init__(self, config, library_manager=None): def find_cores(self, library, ignored_dirs): found_cores = [] + + # Resolve any environmentally-special library locations path = os.path.expanduser(library.location) + if library.sync_type in ["env"]: + from fusesoc.provider import get_provider + + path = get_provider(library.sync_type).resolve(library) + exclude = {".git"} if os.path.isdir(path) == False: raise OSError(path + " is not a directory") diff --git a/fusesoc/librarymanager.py b/fusesoc/librarymanager.py index c56b181a..ebaf81f6 100644 --- a/fusesoc/librarymanager.py +++ b/fusesoc/librarymanager.py @@ -20,7 +20,7 @@ def __init__( sync_version=None, auto_sync=True, ): - if sync_type and not sync_type in ["local", "git"]: + if sync_type and not sync_type in ["local", "git", "env"]: raise ValueError( "Library {} ({}) Invalid sync-type '{}'".format( name, location, sync_type @@ -46,8 +46,8 @@ def update(self, force=False): def l(s): return self.name + " : " + s - if self.sync_type == "local": - logger.info(l("sync-type is local. Ignoring update")) + if self.sync_type in ["local", "env"]: + logger.info(l(f"sync-type is {self.sync_type}. Ignoring update")) return # FIXME: Do an initial checkout if missing diff --git a/fusesoc/main.py b/fusesoc/main.py index a832ee8d..0df32f89 100644 --- a/fusesoc/main.py +++ b/fusesoc/main.py @@ -500,7 +500,7 @@ def get_parser(): parser_library_add.add_argument( "--sync-type", help="The provider type for the library. Defaults to 'git'.", - choices=["git", "local"], + choices=["git", "local", "env"], dest="sync-type", ) parser_library_add.add_argument( diff --git a/fusesoc/provider/env.py b/fusesoc/provider/env.py new file mode 100644 index 00000000..c5e64b85 --- /dev/null +++ b/fusesoc/provider/env.py @@ -0,0 +1,76 @@ +# Copyright FuseSoC contributors +# Licensed under the 2-Clause BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-2-Clause + +import logging +import subprocess +from typing import Callable + +from fusesoc.librarymanager import Library +from fusesoc.provider.provider import Provider +from fusesoc.utils import Launcher + +logger = logging.getLogger(__name__) + + +class Env(Provider): + """A provider for cores in locations special to the environment.""" + + def _resolve_poetry(library: Library) -> str: + logger.info(f"Looking for Poetry environment for library {library.name}") + + err = False + envpath = None + try: + # We need to wrap Poetry in Poetry in case the environment uses a different + # Python interpreter than is currently configured through `poetry env use`. If + # the two differ, then `poetry env info -p` just gives us a blank path. + proc = Launcher("poetry", ["run", "poetry", "env", "info", "--path"]) + proc = proc.run(stdout=subprocess.PIPE) + stdout, _ = proc.communicate() + envpath = stdout.decode("ascii").strip() + except subprocess.CalledProcessError as e: + logger.error("Failed to run `poetry env info`") + err = True + + if envpath and len(envpath) == 0: + logger.error("Poetry returned an empty environment path") + err = True + + if err: + raise RuntimeError("Could not create library from Poetry environment") + + logger.info(f"Using '{envpath}'") + return envpath + + _SYNC_URIS = {"poetry": _resolve_poetry} + + @staticmethod + def resolve(library: Library) -> str: + resolver = Env._SYNC_URIS.get(library.sync_uri, None) + if not resolver: + raise RuntimeError( + "Unsupported sync-uri '{}' for sync-type 'env', options are: {}".format( + library.sync_uri, Env._SYNC_URIS.keys() + ) + ) + + return resolver(library) + + @staticmethod + def init_library(library): + path = Env.resolve(library) + + library.location = None + if not library.auto_sync: + logger.info("Auto-sync disabled, converting to local provider") + library.location = path + library.sync_type = "local" + library.sync_uri = path + + def _checkout(self, local_dir): + pass + + @staticmethod + def update_library(library): + pass