Handle "dependencies" from library and project when build libraries // Issue #709

This commit is contained in:
Ivan Kravets
2016-07-31 00:00:58 +03:00
parent b364389541
commit 74af8a5c39
4 changed files with 108 additions and 36 deletions

View File

@ -39,10 +39,15 @@ How works Library Dependency Finder (LDF)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Library Dependency Finder is a part of PlatformIO Library Build System. It
operates with the header files (``*.h, *.hpp``) and looks for
``#include <...>`` directives. What is more, LDF interprets C Preprocessor
conditional macros (``#ifdef ...``, etc.). Library Dependency Finder starts
work from analyzing source files from :ref:`projectconf_pio_src_dir`.
operates with the C/C++ source files and looks for ``#include <...>``
directives. Also, LDF interprets C Preprocessor conditional macros
(``#if``, ``ifdef``, etc.). Library Dependency Finder starts
work from analyzing source files from :ref:`projectconf_pio_src_dir` by default.
If project or library contains own ``dependencies`` list (see
:ref:`libjson_dependencies`), the LDF will not looking for dependencies in
the source code. The specified libraries will be built automatically without
check.
There are different library storages where Library Dependency Finder looks for
dependencies. These storages/folders have priority. LDF operates in the next

View File

@ -381,6 +381,7 @@ A list of dependent libraries. They will be installed automatically with
Allowed requirements for dependent library:
* ``name`` | Type: ``String``
* ``version`` | Type: ``String``
* ``authors`` | Type: ``String`` or ``Array``
* ``frameworks`` | Type: ``String`` or ``Array``
* ``platforms`` | Type: ``String`` or ``Array``
@ -401,10 +402,23 @@ Example:
},
{
"name": "Library-Bar",
"frameworks": "FrameworkFoo, FrameworkBar"
"version": "~1.2.3"
},
{
"name": "lib-from-repo",
"version": "https://github.com/user/package.git#1.2.3"
}
]
A short definition of dependencies is allowed:
.. code-block:: javascript
"dependencies": {
"mylib": "1.2.3",
"lib-from-repo": "githubuser/package"
}
See more ``library.json`` :ref:`library_creating_examples`.

View File

@ -14,7 +14,7 @@
import sys
VERSION = (3, 0, "0.dev13")
VERSION = (3, 0, "0.dev14")
__version__ = ".".join([str(s) for s in VERSION])
__title__ = "platformio"

View File

@ -82,8 +82,8 @@ class LibBuilderBase(object): # pylint: disable=too-many-instance-attributes
self.path = env.subst(path)
self._manifest = self.load_manifest()
self._is_dependent = False
self._deps = tuple()
self._scanner_visited = tuple()
self._depbuilders = tuple()
self._scanned_paths = tuple()
self._built_node = None
# process extra options and append to build environment
@ -103,6 +103,31 @@ class LibBuilderBase(object): # pylint: disable=too-many-instance-attributes
def version(self):
return self._manifest.get("version")
@property
def dependencies(self):
deps = self._manifest.get("dependencies")
if not deps:
return deps
items = []
if isinstance(deps, dict):
if "name" in deps:
items.append(deps)
else:
for name, version in deps.items():
items.append({"name": name, "version": version})
elif isinstance(deps, list):
items = [d for d in deps if "name" in d]
for item in items:
for k in ("frameworks", "platforms"):
if k not in item or isinstance(k, list):
continue
if item[k] == "*":
del item[k]
elif isinstance(item[k], basestring):
item[k] = [i.strip() for i in item[k].split(",")
if i.strip()]
return items
@property
def src_filter(self):
return piotool.SRC_FILTER_DEFAULT + [
@ -136,8 +161,8 @@ class LibBuilderBase(object): # pylint: disable=too-many-instance-attributes
return True
@property
def dependencies(self):
return self._deps
def depbuilders(self):
return self._depbuilders
@property
def dependent(self):
@ -171,20 +196,20 @@ class LibBuilderBase(object): # pylint: disable=too-many-instance-attributes
assert isinstance(search_paths, tuple)
deep_search = self.env.get("LIB_DEEP_SEARCH", "true").lower() == "true"
if not self._scanner_visited and (
if not self._scanned_paths and (
isinstance(self, ProjectAsLibBuilder) or deep_search):
for item in self.env.MatchSourceFiles(self.src_dir,
self.src_filter):
path = join(self.src_dir, item)
if (path not in self._scanner_visited and
if (path not in self._scanned_paths and
path not in search_paths):
search_paths += (path, )
_search_paths = tuple()
for path in search_paths:
if path not in self._scanner_visited:
if path not in self._scanned_paths:
_search_paths += (path, )
self._scanner_visited += (path, )
self._scanned_paths += (path, )
return _search_paths
@ -207,16 +232,46 @@ class LibBuilderBase(object): # pylint: disable=too-many-instance-attributes
result += (inc, )
return result
def depends_on(self, lb):
def depend_recursive(self, lb, lib_builders, search_paths=None):
assert isinstance(lb, LibBuilderBase)
if self in lb.dependencies:
sys.stderr.write("Warning! Circular dependencies detected "
"between `%s` and `%s`\n" % (self.path, lb.path))
elif lb not in self._deps:
self._deps += (lb, )
if self != lb:
if self in lb.depbuilders:
sys.stderr.write("Warning! Circular dependencies detected "
"between `%s` and `%s`\n" %
(self.path, lb.path))
elif lb not in self._depbuilders:
self._depbuilders += (lb, )
lb.search_deps_recursive(lib_builders, search_paths)
def search_dependencies(self, lib_builders, search_paths=None):
def search_deps_recursive(self, lib_builders, search_paths=None):
self._is_dependent = True
# if dependencies are specified, don't use automatic finder
if self.dependencies:
for item in self.dependencies:
found = False
for lb in lib_builders:
if item['name'] != lb.name:
continue
elif "frameworks" in item and \
not any([lb.is_framework_compatible(f)
for f in item["frameworks"]]):
continue
elif "platforms" in item and \
not any([lb.is_platform_compatible(p)
for p in item["platforms"]]):
continue
found = True
self.depend_recursive(lb, lib_builders)
break
if not found:
sys.stderr.write(
"Error: Could not find `%s` dependency for `%s` "
"library\n" % (item['name'], self.name))
self.env.Exit(2)
return
lib_inc_map = {}
for inc in self._get_found_includes(lib_builders, search_paths):
for lb in lib_builders:
@ -227,13 +282,11 @@ class LibBuilderBase(object): # pylint: disable=too-many-instance-attributes
break
for lb, lb_src_files in lib_inc_map.items():
if lb != self and lb not in self.dependencies:
self.depends_on(lb)
lb.search_dependencies(lib_builders, lb_src_files)
self.depend_recursive(lb, lib_builders, lb_src_files)
def build(self):
libs = []
for lb in self.dependencies:
for lb in self.depbuilders:
libs.extend(lb.build())
# copy shared information to self env
for key in ("CPPPATH", "LIBPATH", "LIBS", "LINKFLAGS"):
@ -265,15 +318,15 @@ class ProjectAsLibBuilder(LibBuilderBase):
# skip for project, options are already processed
pass
def search_dependencies(self, lib_builders, search_paths=None):
def search_deps_recursive(self, lib_builders, search_paths=None):
for lib_name in self.env.get("LIB_FORCE", []):
for lb in lib_builders:
if lb.name == lib_name and lb not in self.dependencies:
self.depends_on(lb)
lb.search_dependencies(lib_builders)
if lb.name == lib_name:
if lb not in self.depbuilders:
self.depend_recursive(lb, lib_builders)
break
return LibBuilderBase.search_dependencies(self, lib_builders,
search_paths)
return LibBuilderBase.search_deps_recursive(self, lib_builders,
search_paths)
def build(self):
# dummy mark that project is built
@ -422,7 +475,7 @@ def GetLibBuilders(env):
lb = LibBuilderFactory.new(env, join(libs_dir, item))
if lb.name in env.get("LIB_IGNORE", []):
if not env.GetOption("silent"):
print "Ignored library " + lb.path
sys.stderr.write("Ignored library %s\n" % lb.path)
continue
if compat_level > 1 and not lb.is_platform_compatible(env[
'PIOPLATFORM']):
@ -444,14 +497,14 @@ def BuildDependentLibraries(env, src_dir):
def print_deps_tree(root, level=0):
margin = "| " * (level)
for lb in root.dependencies:
for lb in root.depbuilders:
title = "<%s>" % lb.name
if lb.version:
title += " v%s" % lb.version
if not env.GetOption("silent"):
title += " (%s)" % lb.path
print "%s|-- %s" % (margin, title)
if lb.dependencies:
if lb.depbuilders:
print_deps_tree(lb, level + 1)
lib_builders = env.GetLibBuilders()
@ -461,9 +514,9 @@ def BuildDependentLibraries(env, src_dir):
project = ProjectAsLibBuilder(env, src_dir)
project.env = env
project.search_dependencies(lib_builders)
project.search_deps_recursive(lib_builders)
if project.dependencies:
if project.depbuilders:
print "Library Dependency Map"
print_deps_tree(project)
else: