@@ -194,6 +194,8 @@ def __init__(self, mode, path_vars=None):
194194 self ._visited = set ()
195195 # Stack of metadata for each level.
196196 self ._metadata = [ManifestPackageMetadata ()]
197+ # Registered external libraries.
198+ self ._libraries = {}
197199
198200 def _resolve_path (self , path ):
199201 # Convert path to an absolute path, applying variable substitutions.
@@ -208,6 +210,7 @@ def _manifest_globals(self, kwargs):
208210 "metadata" : self .metadata ,
209211 "include" : self .include ,
210212 "require" : self .require ,
213+ "add_library" : self .add_library ,
211214 "package" : self .package ,
212215 "module" : self .module ,
213216 "options" : IncludeOptions (** kwargs ),
@@ -388,14 +391,25 @@ def include(self, manifest_path, is_require=False, **kwargs):
388391 if is_require :
389392 self ._metadata .pop ()
390393
391- def require (self , name , version = None , unix_ffi = False , pypi = None , ** kwargs ):
394+ def _require_from_path (self , library_path , name , version , extra_kwargs ):
395+ for root , dirnames , filenames in os .walk (library_path ):
396+ if os .path .basename (root ) == name and "manifest.py" in filenames :
397+ self .include (root , is_require = True , ** extra_kwargs )
398+ return True
399+ return False
400+
401+ def require (self , name , version = None , unix_ffi = False , pypi = None , library = None , ** kwargs ):
392402 """
393- Require a module by name from micropython-lib.
403+ Require a package by name from micropython-lib.
394404
395405 Optionally specify unix_ffi=True to use a module from the unix-ffi directory.
396406
397407 Optionally specify pipy="package-name" to indicate that this should
398408 use the named package from PyPI when building for CPython.
409+
410+ Optionally specify library="name" to reference a package from a
411+ library that has been previously registered with add_library(). Otherwise
412+ micropython-lib will be used.
399413 """
400414 self ._metadata [- 1 ].check_initialised (self ._mode )
401415
@@ -406,27 +420,46 @@ def require(self, name, version=None, unix_ffi=False, pypi=None, **kwargs):
406420 self ._pypi_dependencies .append (pypi )
407421 return
408422
409- if self ._path_vars ["MPY_LIB_DIR" ]:
423+ if library is not None :
424+ # Find package in external library.
425+ if library not in self ._libraries :
426+ raise ValueError ("Unknown library '{}' for require('{}')." .format (library , name ))
427+ library_path = self ._libraries [library ]
428+ # Search for {library_path}/**/{name}/manifest.py.
429+ if not self ._require_from_path (library_path , name , version , kwargs ):
430+ raise ValueError (
431+ "Package '{}' not found in external library '{}' ({})." .format (
432+ name , library , library_path
433+ )
434+ )
435+ elif self ._path_vars ["MPY_LIB_DIR" ]:
436+ # Find package in micropython-lib, in one of the three top-level directories.
410437 lib_dirs = ["micropython" , "python-stdlib" , "python-ecosys" ]
411438 if unix_ffi :
412- # Search unix-ffi only if unix_ffi=True, and make unix-ffi modules
439+ # Additionally search unix-ffi only if unix_ffi=True, and make unix-ffi modules
413440 # take precedence.
414441 lib_dirs = ["unix-ffi" ] + lib_dirs
415442
416443 for lib_dir in lib_dirs :
417444 # Search for {lib_dir}/**/{name}/manifest.py.
418- for root , dirnames , filenames in os . walk (
419- os .path .join (self ._path_vars ["MPY_LIB_DIR" ], lib_dir )
445+ if self . _require_from_path (
446+ os .path .join (self ._path_vars ["MPY_LIB_DIR" ], lib_dir ), name , version , kwargs
420447 ):
421- if os .path .basename (root ) == name and "manifest.py" in filenames :
422- self .include (root , is_require = True , ** kwargs )
423- return
448+ return
424449
425- raise ValueError ("Library not found in local micropython-lib: {} " .format (name ))
450+ raise ValueError ("Package '{}' not found in local micropython-lib. " .format (name ))
426451 else :
427452 # TODO: HTTP request to obtain URLs from manifest.json.
428453 raise ValueError ("micropython-lib not available for require('{}')." , name )
429454
455+ def add_library (self , library , library_path ):
456+ """
457+ Register the path to an external named library.
458+
459+ This allows require("name", library="library") to find packages in that library.
460+ """
461+ self ._libraries [library ] = self ._resolve_path (library_path )
462+
430463 def package (self , package_path , files = None , base_path = "." , opt = None ):
431464 """
432465 Define a package, optionally restricting to a set of files.
0 commit comments