LDF: handle "include" folder per project; fix issue with header files // Resolve #1235

This commit is contained in:
Ivan Kravets
2017-12-16 15:38:13 +02:00
parent 25341d1ec7
commit 3d8183a2e1
5 changed files with 119 additions and 84 deletions

View File

@@ -32,7 +32,7 @@ def _dump_includes(env):
# installed libs # installed libs
for lb in env.GetLibBuilders(): for lb in env.GetLibBuilders():
includes.extend(lb.get_inc_dirs()) includes.extend(lb.get_include_dirs())
# includes from toolchains # includes from toolchains
p = env.PioPlatform() p = env.PioPlatform()

View File

@@ -24,7 +24,7 @@ from os.path import (basename, commonprefix, dirname, isdir, isfile, join,
from platform import system from platform import system
import SCons.Scanner import SCons.Scanner
from SCons.Script import ARGUMENTS, DefaultEnvironment from SCons.Script import ARGUMENTS, COMMAND_LINE_TARGETS, DefaultEnvironment
from platformio import util from platformio import util
from platformio.builder.tools import platformio as piotool from platformio.builder.tools import platformio as piotool
@@ -89,7 +89,7 @@ class LibBuilderBase(object):
CLASSIC_SCANNER = SCons.Scanner.C.CScanner() CLASSIC_SCANNER = SCons.Scanner.C.CScanner()
ADVANCED_SCANNER = SCons.Scanner.C.CScanner(advanced=True) ADVANCED_SCANNER = SCons.Scanner.C.CScanner(advanced=True)
PARSE_SRC_BY_H_NAME = True PARSE_SRC_BY_H_NAME = True
_INC_DIRS_CACHE = None _INCLUDE_DIRS_CACHE = None
def __init__(self, env, path, manifest=None, verbose=False): def __init__(self, env, path, manifest=None, verbose=False):
self.env = env.Clone() self.env = env.Clone()
@@ -102,7 +102,7 @@ class LibBuilderBase(object):
self._is_built = False self._is_built = False
self._depbuilders = list() self._depbuilders = list()
self._circular_deps = list() self._circular_deps = list()
self._scanned_paths = list() self._processed_files = list()
# reset source filter, could be overridden with extra script # reset source filter, could be overridden with extra script
self.env['SRC_FILTER'] = "" self.env['SRC_FILTER'] = ""
@@ -143,21 +143,28 @@ class LibBuilderBase(object):
"-<tests%s>" % os.sep "-<tests%s>" % os.sep
] ]
@property
def include_dir(self):
if not all([isdir(join(self.path, d)) for d in ("include", "src")]):
return None
return join(self.path, "include")
@property @property
def src_dir(self): def src_dir(self):
return (join(self.path, "src") return (join(self.path, "src")
if isdir(join(self.path, "src")) else self.path) if isdir(join(self.path, "src")) else self.path)
def get_include_dirs(self):
items = [self.src_dir]
include_dir = self.include_dir
if include_dir and include_dir not in items:
items.append(include_dir)
return items
@property @property
def build_dir(self): def build_dir(self):
return join("$BUILD_DIR", "lib", basename(self.path)) return join("$BUILD_DIR", "lib", basename(self.path))
def get_inc_dirs(self):
items = [self.src_dir]
if all([isdir(join(self.path, d)) for d in ("include", "src")]):
items.append(join(self.path, "include"))
return items
@property @property
def build_flags(self): def build_flags(self):
return None return None
@@ -239,13 +246,6 @@ class LibBuilderBase(object):
def load_manifest(self): def load_manifest(self):
return {} return {}
def get_src_files(self):
return [
join(self.src_dir, item)
for item in self.env.MatchSourceFiles(self.src_dir,
self.src_filter)
]
def process_extra_options(self): def process_extra_options(self):
with util.cd(self.path): with util.cd(self.path):
self.env.ProcessUnFlags(self.build_unflags) self.env.ProcessUnFlags(self.build_unflags)
@@ -298,44 +298,60 @@ class LibBuilderBase(object):
"library\n" % (item['name'], self.name)) "library\n" % (item['name'], self.name))
self.env.Exit(1) self.env.Exit(1)
def _validate_search_paths(self, search_paths=None): def get_search_files(self):
if not search_paths: items = [
search_paths = [] join(self.src_dir, item)
assert isinstance(search_paths, list) for item in self.env.MatchSourceFiles(self.src_dir,
self.src_filter)
]
include_dir = self.include_dir
if include_dir:
items.extend([
join(include_dir, item)
for item in self.env.MatchSourceFiles(include_dir)
])
return items
_search_paths = [] def _validate_search_files(self, search_files=None):
for path in search_paths: if not search_files:
if path not in self._scanned_paths: search_files = []
_search_paths.append(path) assert isinstance(search_files, list)
self._scanned_paths.append(path)
return _search_paths _search_files = []
for path in search_files:
if path not in self._processed_files:
_search_files.append(path)
self._processed_files.append(path)
def _get_found_includes(self, search_paths=None): return _search_files
def _get_found_includes(self, search_files=None):
# all include directories # all include directories
if not LibBuilderBase._INC_DIRS_CACHE: if not LibBuilderBase._INCLUDE_DIRS_CACHE:
LibBuilderBase._INC_DIRS_CACHE = [] LibBuilderBase._INCLUDE_DIRS_CACHE = []
for lb in self.env.GetLibBuilders(): for lb in self.env.GetLibBuilders():
LibBuilderBase._INC_DIRS_CACHE.extend( LibBuilderBase._INCLUDE_DIRS_CACHE.extend(
[self.env.Dir(d) for d in lb.get_inc_dirs()]) [self.env.Dir(d) for d in lb.get_include_dirs()])
# append self include directories # append self include directories
inc_dirs = [self.env.Dir(d) for d in self.get_inc_dirs()] include_dirs = [self.env.Dir(d) for d in self.get_include_dirs()]
inc_dirs.extend(LibBuilderBase._INC_DIRS_CACHE) include_dirs.extend(LibBuilderBase._INCLUDE_DIRS_CACHE)
result = [] result = []
for path in self._validate_search_paths(search_paths): for path in self._validate_search_files(search_files):
try: try:
assert "+" in self.lib_ldf_mode assert "+" in self.lib_ldf_mode
incs = self.env.File(path).get_found_includes( incs = self.env.File(path).get_found_includes(
self.env, LibBuilderBase.ADVANCED_SCANNER, tuple(inc_dirs)) self.env, LibBuilderBase.ADVANCED_SCANNER,
tuple(include_dirs))
except Exception as e: # pylint: disable=broad-except except Exception as e: # pylint: disable=broad-except
if self.verbose and "+" in self.lib_ldf_mode: if self.verbose and "+" in self.lib_ldf_mode:
sys.stderr.write( sys.stderr.write(
"Warning! Classic Pre Processor is used for `%s`, " "Warning! Classic Pre Processor is used for `%s`, "
"advanced has failed with `%s`\n" % (path, e)) "advanced has failed with `%s`\n" % (path, e))
_incs = self.env.File(path).get_found_includes( _incs = self.env.File(path).get_found_includes(
self.env, LibBuilderBase.CLASSIC_SCANNER, tuple(inc_dirs)) self.env, LibBuilderBase.CLASSIC_SCANNER,
tuple(include_dirs))
incs = [] incs = []
for inc in _incs: for inc in _incs:
incs.append(inc) incs.append(inc)
@@ -356,7 +372,7 @@ class LibBuilderBase(object):
result.append(inc) result.append(inc)
return result return result
def depend_recursive(self, lb, search_paths=None): def depend_recursive(self, lb, search_files=None):
def _already_depends(_lb): def _already_depends(_lb):
if self in _lb.depbuilders: if self in _lb.depbuilders:
@@ -376,23 +392,23 @@ class LibBuilderBase(object):
self._circular_deps.append(lb) self._circular_deps.append(lb)
elif lb not in self._depbuilders: elif lb not in self._depbuilders:
self._depbuilders.append(lb) self._depbuilders.append(lb)
LibBuilderBase._INC_DIRS_CACHE = None LibBuilderBase._INCLUDE_DIRS_CACHE = None
lb.search_deps_recursive(search_paths) lb.search_deps_recursive(search_files)
def search_deps_recursive(self, search_paths=None): def search_deps_recursive(self, search_files=None):
if not self._is_dependent: if not self._is_dependent:
self._is_dependent = True self._is_dependent = True
self.process_dependencies() self.process_dependencies()
if self.lib_ldf_mode.startswith("deep"): if self.lib_ldf_mode.startswith("deep"):
search_paths = self.get_src_files() search_files = self.get_search_files()
# when LDF is disabled # when LDF is disabled
if self.lib_ldf_mode == "off": if self.lib_ldf_mode == "off":
return return
lib_inc_map = {} lib_inc_map = {}
for inc in self._get_found_includes(search_paths): for inc in self._get_found_includes(search_files):
for lb in self.env.GetLibBuilders(): for lb in self.env.GetLibBuilders():
if inc.get_abspath() in lb: if inc.get_abspath() in lb:
if lb not in lib_inc_map: if lb not in lib_inc_map:
@@ -400,8 +416,8 @@ class LibBuilderBase(object):
lib_inc_map[lb].append(inc.get_abspath()) lib_inc_map[lb].append(inc.get_abspath())
break break
for lb, lb_search_paths in lib_inc_map.items(): for lb, lb_search_files in lib_inc_map.items():
self.depend_recursive(lb, lb_search_paths) self.depend_recursive(lb, lb_search_files)
def build(self): def build(self):
libs = [] libs = []
@@ -412,13 +428,13 @@ class LibBuilderBase(object):
self.env.AppendUnique(**{key: lb.env.get(key)}) self.env.AppendUnique(**{key: lb.env.get(key)})
for lb in self._circular_deps: for lb in self._circular_deps:
self.env.AppendUnique(CPPPATH=lb.get_inc_dirs()) self.env.AppendUnique(CPPPATH=lb.get_include_dirs())
if self._is_built: if self._is_built:
return libs return libs
self._is_built = True self._is_built = True
self.env.AppendUnique(CPPPATH=self.get_inc_dirs()) self.env.AppendUnique(CPPPATH=self.get_include_dirs())
if self.lib_ldf_mode == "off": if self.lib_ldf_mode == "off":
for lb in self.env.GetLibBuilders(): for lb in self.env.GetLibBuilders():
@@ -455,13 +471,13 @@ class ArduinoLibBuilder(LibBuilderBase):
manifest[key.strip()] = value.strip() manifest[key.strip()] = value.strip()
return manifest return manifest
def get_inc_dirs(self): def get_include_dirs(self):
inc_dirs = LibBuilderBase.get_inc_dirs(self) include_dirs = LibBuilderBase.get_include_dirs(self)
if isdir(join(self.path, "src")): if isdir(join(self.path, "src")):
return inc_dirs return include_dirs
if isdir(join(self.path, "utility")): if isdir(join(self.path, "utility")):
inc_dirs.append(join(self.path, "utility")) include_dirs.append(join(self.path, "utility"))
return inc_dirs return include_dirs
@property @property
def src_filter(self): def src_filter(self):
@@ -486,19 +502,25 @@ class MbedLibBuilder(LibBuilderBase):
return {} return {}
return util.load_json(join(self.path, "module.json")) return util.load_json(join(self.path, "module.json"))
@property
def include_dir(self):
if isdir(join(self.path, "include")):
return join(self.path, "include")
return None
@property @property
def src_dir(self): def src_dir(self):
if isdir(join(self.path, "source")): if isdir(join(self.path, "source")):
return join(self.path, "source") return join(self.path, "source")
return LibBuilderBase.src_dir.fget(self) return LibBuilderBase.src_dir.fget(self)
def get_inc_dirs(self): def get_include_dirs(self):
inc_dirs = LibBuilderBase.get_inc_dirs(self) include_dirs = LibBuilderBase.get_include_dirs(self)
if self.path not in inc_dirs: if self.path not in include_dirs:
inc_dirs.append(self.path) include_dirs.append(self.path)
for p in self._manifest.get("extraIncludes", []): for p in self._manifest.get("extraIncludes", []):
inc_dirs.append(join(self.path, p)) include_dirs.append(join(self.path, p))
return inc_dirs return include_dirs
def is_frameworks_compatible(self, frameworks): def is_frameworks_compatible(self, frameworks):
return self.items_in_list(frameworks, ["mbed"]) return self.items_in_list(frameworks, ["mbed"])
@@ -592,34 +614,48 @@ class PlatformIOLibBuilder(LibBuilderBase):
return LibBuilderBase.is_frameworks_compatible(self, frameworks) return LibBuilderBase.is_frameworks_compatible(self, frameworks)
return self.items_in_list(frameworks, items) return self.items_in_list(frameworks, items)
def get_inc_dirs(self): def get_include_dirs(self):
inc_dirs = LibBuilderBase.get_inc_dirs(self) include_dirs = LibBuilderBase.get_include_dirs(self)
# backwards compatibility with PlatformIO 2.0 # backwards compatibility with PlatformIO 2.0
if ("build" not in self._manifest and self._is_arduino_manifest() if ("build" not in self._manifest and self._is_arduino_manifest()
and not isdir(join(self.path, "src")) and not isdir(join(self.path, "src"))
and isdir(join(self.path, "utility"))): and isdir(join(self.path, "utility"))):
inc_dirs.append(join(self.path, "utility")) include_dirs.append(join(self.path, "utility"))
for path in self.env.get("CPPPATH", []): for path in self.env.get("CPPPATH", []):
if path not in self.envorigin.get("CPPPATH", []): if path not in self.envorigin.get("CPPPATH", []):
inc_dirs.append(self.env.subst(path)) include_dirs.append(self.env.subst(path))
return inc_dirs return include_dirs
class ProjectAsLibBuilder(LibBuilderBase): class ProjectAsLibBuilder(LibBuilderBase):
@property
def include_dir(self):
include_dir = self.env.subst("$PROJECTINCLUDE_DIR")
return include_dir if isdir(include_dir) else None
@property @property
def src_dir(self): def src_dir(self):
return self.env.subst("$PROJECTSRC_DIR") return self.env.subst("$PROJECTSRC_DIR")
def get_inc_dirs(self): def get_include_dirs(self):
inc_dirs = LibBuilderBase.get_inc_dirs(self) include_dirs = LibBuilderBase.get_include_dirs(self)
inc_dirs.append(self.env.subst("$PROJECTINCLUDE_DIR")) include_dirs.append(self.env.subst("$PROJECTINCLUDE_DIR"))
return inc_dirs return include_dirs
def get_src_files(self): def get_search_files(self):
return self.env.get("PROJECTBUILDFILES", []) # project files
items = LibBuilderBase.get_search_files(self)
# test files
if "__test" in COMMAND_LINE_TARGETS:
items.extend([
join("$PROJECTTEST_DIR", item)
for item in self.env.MatchSourceFiles("$PROJECTTEST_DIR",
"$PIOTEST_SRC_FILTER")
])
return items
@property @property
def lib_ldf_mode(self): def lib_ldf_mode(self):
@@ -673,7 +709,7 @@ class ProjectAsLibBuilder(LibBuilderBase):
def build(self): def build(self):
self._is_built = True # do not build Project now self._is_built = True # do not build Project now
self.env.AppendUnique(CPPPATH=self.get_inc_dirs()) self.env.AppendUnique(CPPPATH=self.get_include_dirs())
return LibBuilderBase.build(self) return LibBuilderBase.build(self)
@@ -753,7 +789,7 @@ def BuildProjectLibraries(env):
found_lbs = [lb for lb in lib_builders if lb.dependent] found_lbs = [lb for lb in lib_builders if lb.dependent]
for lb in lib_builders: for lb in lib_builders:
if lb in found_lbs: if lb in found_lbs:
lb.search_deps_recursive(lb.get_src_files()) lb.search_deps_recursive(lb.get_search_files())
for lb in lib_builders: for lb in lib_builders:
for deplb in lb.depbuilders[:]: for deplb in lb.depbuilders[:]:
if deplb not in found_lbs: if deplb not in found_lbs:

View File

@@ -293,11 +293,12 @@ def ProcessTest(env):
src_filter = ["+<*.cpp>", "+<*.c>"] src_filter = ["+<*.cpp>", "+<*.c>"]
if "PIOTEST" in env: if "PIOTEST" in env:
src_filter.append("+<%s%s>" % (env['PIOTEST'], sep)) src_filter.append("+<%s%s>" % (env['PIOTEST'], sep))
env.Replace(PIOTEST_SRC_FILTER=src_filter)
return env.CollectBuildFiles( return env.CollectBuildFiles(
"$BUILDTEST_DIR", "$BUILDTEST_DIR",
"$PROJECTTEST_DIR", "$PROJECTTEST_DIR",
src_filter=src_filter, "$PIOTEST_SRC_FILTER",
duplicate=False) duplicate=False)

View File

@@ -63,16 +63,14 @@ def BuildProgram(env):
# Search for project source files # Search for project source files
env.Append( env.Append(
LIBPATH=["$BUILD_DIR"], LIBPATH=["$BUILD_DIR"],
PROJECTBUILDFILES=env.CollectBuildFiles( PIOBUILDFILES=env.CollectBuildFiles(
"$BUILDSRC_DIR", "$BUILDSRC_DIR", "$PROJECTSRC_DIR", "$SRC_FILTER",
"$PROJECTSRC_DIR",
src_filter=env.get("SRC_FILTER"),
duplicate=False)) duplicate=False))
if "__debug" in COMMAND_LINE_TARGETS: if "__debug" in COMMAND_LINE_TARGETS:
env.ProcessDebug() env.ProcessDebug()
if "__test" in COMMAND_LINE_TARGETS: if "__test" in COMMAND_LINE_TARGETS:
env.Append(PROJECTBUILDFILES=env.ProcessTest()) env.Append(PIOBUILDFILES=env.ProcessTest())
# build dependent libs # build dependent libs
env.Append(LIBS=env.BuildProjectLibraries()) env.Append(LIBS=env.BuildProjectLibraries())
@@ -90,15 +88,14 @@ def BuildProgram(env):
# Handle SRC_BUILD_FLAGS # Handle SRC_BUILD_FLAGS
env.ProcessFlags(env.get("SRC_BUILD_FLAGS")) env.ProcessFlags(env.get("SRC_BUILD_FLAGS"))
if not env.get("PROJECTBUILDFILES") and not COMMAND_LINE_TARGETS: if not env.get("PIOBUILDFILES") and not COMMAND_LINE_TARGETS:
sys.stderr.write( sys.stderr.write(
"Error: Nothing to build. Please put your source code files " "Error: Nothing to build. Please put your source code files "
"to '%s' folder\n" % env.subst("$PROJECTSRC_DIR")) "to '%s' folder\n" % env.subst("$PROJECTSRC_DIR"))
env.Exit(1) env.Exit(1)
program = env.Program( program = env.Program(
join("$BUILD_DIR", env.subst("$PROGNAME")), join("$BUILD_DIR", env.subst("$PROGNAME")), env['PIOBUILDFILES'])
env['PROJECTBUILDFILES'] + env.get("PIOBUILDFILES", []))
checksize_action = Action(env.CheckUploadSize, "Checking program size") checksize_action = Action(env.CheckUploadSize, "Checking program size")
AlwaysBuild(env.Alias("checkprogsize", program, checksize_action)) AlwaysBuild(env.Alias("checkprogsize", program, checksize_action))
@@ -186,6 +183,7 @@ def MatchSourceFiles(env, src_dir, src_filter=None):
items.add(item.replace(src_dir + sep, "")) items.add(item.replace(src_dir + sep, ""))
src_dir = env.subst(src_dir) src_dir = env.subst(src_dir)
src_filter = env.subst(src_filter) if src_filter else None
src_filter = src_filter or SRC_FILTER_DEFAULT src_filter = src_filter or SRC_FILTER_DEFAULT
if isinstance(src_filter, (list, tuple)): if isinstance(src_filter, (list, tuple)):
src_filter = " ".join(src_filter) src_filter = " ".join(src_filter)
@@ -270,12 +268,12 @@ def BuildLibrary(env, variant_dir, src_dir, src_filter=None):
lib = env.Clone() lib = env.Clone()
return lib.StaticLibrary( return lib.StaticLibrary(
lib.subst(variant_dir), lib.subst(variant_dir),
lib.CollectBuildFiles(variant_dir, src_dir, src_filter=src_filter)) lib.CollectBuildFiles(variant_dir, src_dir, src_filter))
def BuildSources(env, variant_dir, src_dir, src_filter=None): def BuildSources(env, variant_dir, src_dir, src_filter=None):
DefaultEnvironment().Append(PIOBUILDFILES=env.Clone().CollectBuildFiles( DefaultEnvironment().Append(PIOBUILDFILES=env.Clone().CollectBuildFiles(
variant_dir, src_dir, src_filter=src_filter)) variant_dir, src_dir, src_filter))
def exists(_): def exists(_):