e78bf51f68
Issue #4762 separated out the optional addition of toolchains to compiledb include paths and added a call to it in the library building step for user defined libraries, allowing the automatic inclusion of toolchains in private lib compiledb generation
399 lines
13 KiB
Python
399 lines
13 KiB
Python
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import fnmatch
|
|
import os
|
|
import sys
|
|
|
|
from SCons import Builder, Util # pylint: disable=import-error
|
|
from SCons.Node import FS # pylint: disable=import-error
|
|
from SCons.Script import COMMAND_LINE_TARGETS # pylint: disable=import-error
|
|
from SCons.Script import AlwaysBuild # pylint: disable=import-error
|
|
from SCons.Script import DefaultEnvironment # pylint: disable=import-error
|
|
from SCons.Script import SConscript # pylint: disable=import-error
|
|
|
|
from platformio import __version__, fs
|
|
from platformio.compat import IS_MACOS, string_types
|
|
from platformio.package.version import pepver_to_semver
|
|
from platformio.proc import where_is_program
|
|
|
|
SRC_HEADER_EXT = ["h", "hpp"]
|
|
SRC_ASM_EXT = ["S", "spp", "SPP", "sx", "s", "asm", "ASM"]
|
|
SRC_C_EXT = ["c"]
|
|
SRC_CXX_EXT = ["cc", "cpp", "cxx", "c++"]
|
|
SRC_BUILD_EXT = SRC_C_EXT + SRC_CXX_EXT + SRC_ASM_EXT
|
|
SRC_FILTER_DEFAULT = ["+<*>", "-<.git%s>" % os.sep, "-<.svn%s>" % os.sep]
|
|
|
|
|
|
def scons_patched_match_splitext(path, suffixes=None):
|
|
"""Patch SCons Builder, append $OBJSUFFIX to the end of each target"""
|
|
tokens = Util.splitext(path)
|
|
if suffixes and tokens[1] and tokens[1] in suffixes:
|
|
return (path, tokens[1])
|
|
return tokens
|
|
|
|
|
|
def GetBuildType(env):
|
|
modes = []
|
|
if (
|
|
set(["__debug", "sizedata"]) # sizedata = for memory inspection
|
|
& set(COMMAND_LINE_TARGETS)
|
|
or env.GetProjectOption("build_type") == "debug"
|
|
):
|
|
modes.append("debug")
|
|
if "__test" in COMMAND_LINE_TARGETS or env.GetProjectOption("build_type") == "test":
|
|
modes.append("test")
|
|
return "+".join(modes or ["release"])
|
|
|
|
|
|
def BuildProgram(env):
|
|
env.ProcessProgramDeps()
|
|
env.ProcessProjectDeps()
|
|
|
|
# append into the beginning a main LD script
|
|
if env.get("LDSCRIPT_PATH") and not any("-Wl,-T" in f for f in env["LINKFLAGS"]):
|
|
env.Prepend(LINKFLAGS=["-T", env.subst("$LDSCRIPT_PATH")])
|
|
|
|
# enable "cyclic reference" for linker
|
|
if (
|
|
env.get("LIBS")
|
|
and env.GetCompilerType() == "gcc"
|
|
and (env.PioPlatform().is_embedded() or not IS_MACOS)
|
|
):
|
|
env.Prepend(_LIBFLAGS="-Wl,--start-group ")
|
|
env.Append(_LIBFLAGS=" -Wl,--end-group")
|
|
|
|
program = env.Program(env.subst("$PROGPATH"), env["PIOBUILDFILES"])
|
|
env.Replace(PIOMAINPROG=program)
|
|
|
|
AlwaysBuild(
|
|
env.Alias(
|
|
"checkprogsize",
|
|
program,
|
|
env.VerboseAction(env.CheckUploadSize, "Checking size $PIOMAINPROG"),
|
|
)
|
|
)
|
|
|
|
print("Building in %s mode" % env["BUILD_TYPE"])
|
|
|
|
return program
|
|
|
|
|
|
def ProcessProgramDeps(env):
|
|
def _append_pio_macros():
|
|
core_version = pepver_to_semver(__version__)
|
|
env.AppendUnique(
|
|
CPPDEFINES=[
|
|
(
|
|
"PLATFORMIO",
|
|
int(
|
|
"{0:02d}{1:02d}{2:02d}".format(
|
|
core_version.major, core_version.minor, core_version.patch
|
|
)
|
|
),
|
|
)
|
|
]
|
|
)
|
|
|
|
_append_pio_macros()
|
|
|
|
env.PrintConfiguration()
|
|
|
|
# process extra flags from board
|
|
if "BOARD" in env and "build.extra_flags" in env.BoardConfig():
|
|
env.ProcessFlags(env.BoardConfig().get("build.extra_flags"))
|
|
|
|
# apply user flags
|
|
env.ProcessFlags(env.get("BUILD_FLAGS"))
|
|
|
|
# process framework scripts
|
|
env.BuildFrameworks(env.get("PIOFRAMEWORK"))
|
|
|
|
if "debug" in env["BUILD_TYPE"]:
|
|
env.ConfigureDebugTarget()
|
|
|
|
# remove specified flags
|
|
env.ProcessUnFlags(env.get("BUILD_UNFLAGS"))
|
|
|
|
env.ProcessCompileDbToolchainOption()
|
|
|
|
|
|
def ProcessCompileDbToolchainOption(
|
|
env,
|
|
): # separated out to selectively add to lib build step
|
|
if "compiledb" in COMMAND_LINE_TARGETS:
|
|
# Resolve absolute path of toolchain
|
|
for cmd in ("CC", "CXX", "AS"):
|
|
if cmd not in env:
|
|
continue
|
|
if os.path.isabs(env[cmd]):
|
|
continue
|
|
env[cmd] = where_is_program(
|
|
env.subst("$%s" % cmd), env.subst("${ENV['PATH']}")
|
|
)
|
|
|
|
if env.get("COMPILATIONDB_INCLUDE_TOOLCHAIN"):
|
|
print("Warning! `COMPILATIONDB_INCLUDE_TOOLCHAIN` is scoping")
|
|
for scope, includes in env.DumpIntegrationIncludes().items():
|
|
if scope in ("toolchain",):
|
|
env.Append(CPPPATH=includes)
|
|
|
|
|
|
def ProcessProjectDeps(env):
|
|
plb = env.ConfigureProjectLibBuilder()
|
|
|
|
# prepend project libs to the beginning of list
|
|
env.Prepend(LIBS=plb.build())
|
|
# prepend extra linker related options from libs
|
|
env.PrependUnique(
|
|
**{
|
|
key: plb.env.get(key)
|
|
for key in ("LIBS", "LIBPATH", "LINKFLAGS")
|
|
if plb.env.get(key)
|
|
}
|
|
)
|
|
|
|
if "test" in env["BUILD_TYPE"]:
|
|
build_files_before_nums = len(env.get("PIOBUILDFILES", []))
|
|
plb.env.BuildSources(
|
|
"$BUILD_TEST_DIR", "$PROJECT_TEST_DIR", "$PIOTEST_SRC_FILTER"
|
|
)
|
|
if len(env.get("PIOBUILDFILES", [])) - build_files_before_nums < 1:
|
|
sys.stderr.write(
|
|
"Error: Nothing to build. Please put your test suites "
|
|
"to the '%s' folder\n" % env.subst("$PROJECT_TEST_DIR")
|
|
)
|
|
env.Exit(1)
|
|
|
|
if "test" not in env["BUILD_TYPE"] or env.GetProjectOption("test_build_src"):
|
|
plb.env.BuildSources(
|
|
"$BUILD_SRC_DIR", "$PROJECT_SRC_DIR", env.get("SRC_FILTER")
|
|
)
|
|
|
|
if not env.get("PIOBUILDFILES") and not COMMAND_LINE_TARGETS:
|
|
sys.stderr.write(
|
|
"Error: Nothing to build. Please put your source code files "
|
|
"to the '%s' folder\n" % env.subst("$PROJECT_SRC_DIR")
|
|
)
|
|
env.Exit(1)
|
|
|
|
|
|
def ParseFlagsExtended(env, flags): # pylint: disable=too-many-branches
|
|
if not isinstance(flags, list):
|
|
flags = [flags]
|
|
result = {}
|
|
for raw in flags:
|
|
for key, value in env.ParseFlags(str(raw)).items():
|
|
if key not in result:
|
|
result[key] = []
|
|
result[key].extend(value)
|
|
|
|
cppdefines = []
|
|
for item in result["CPPDEFINES"]:
|
|
if not Util.is_Sequence(item):
|
|
cppdefines.append(item)
|
|
continue
|
|
name, value = item[:2]
|
|
if '"' in value:
|
|
value = value.replace('"', '\\"')
|
|
elif value.isdigit():
|
|
value = int(value)
|
|
elif value.replace(".", "", 1).isdigit():
|
|
value = float(value)
|
|
cppdefines.append((name, value))
|
|
result["CPPDEFINES"] = cppdefines
|
|
|
|
# fix relative CPPPATH & LIBPATH
|
|
for k in ("CPPPATH", "LIBPATH"):
|
|
for i, p in enumerate(result.get(k, [])):
|
|
p = env.subst(p)
|
|
if os.path.isdir(p):
|
|
result[k][i] = os.path.abspath(p)
|
|
|
|
# fix relative path for "-include"
|
|
for i, f in enumerate(result.get("CCFLAGS", [])):
|
|
if isinstance(f, tuple) and f[0] == "-include":
|
|
result["CCFLAGS"][i] = (f[0], env.subst(f[1].get_path()))
|
|
|
|
return result
|
|
|
|
|
|
def ProcessFlags(env, flags): # pylint: disable=too-many-branches
|
|
if not flags:
|
|
return
|
|
env.Append(**env.ParseFlagsExtended(flags))
|
|
|
|
# Cancel any previous definition of name, either built in or
|
|
# provided with a -U option // Issue #191
|
|
undefines = [
|
|
u
|
|
for u in env.get("CCFLAGS", [])
|
|
if isinstance(u, string_types) and u.startswith("-U")
|
|
]
|
|
if undefines:
|
|
for undef in undefines:
|
|
env["CCFLAGS"].remove(undef)
|
|
if undef[2:] in env["CPPDEFINES"]:
|
|
env["CPPDEFINES"].remove(undef[2:])
|
|
env.Append(_CPPDEFFLAGS=" %s" % " ".join(undefines))
|
|
|
|
|
|
def ProcessUnFlags(env, flags):
|
|
if not flags:
|
|
return
|
|
parsed = env.ParseFlagsExtended(flags)
|
|
unflag_scopes = tuple(set(["ASPPFLAGS"] + list(parsed.keys())))
|
|
for scope in unflag_scopes:
|
|
for unflags in parsed.values():
|
|
for unflag in unflags:
|
|
for current in list(env.get(scope, [])):
|
|
conditions = [
|
|
unflag == current,
|
|
not isinstance(unflag, (tuple, list))
|
|
and isinstance(current, (tuple, list))
|
|
and unflag == current[0],
|
|
]
|
|
if any(conditions):
|
|
env[scope].remove(current)
|
|
|
|
|
|
def StringifyMacro(env, value): # pylint: disable=unused-argument
|
|
return '\\"%s\\"' % value.replace('"', '\\\\\\"')
|
|
|
|
|
|
def MatchSourceFiles(env, src_dir, src_filter=None, src_exts=None):
|
|
src_filter = env.subst(src_filter) if src_filter else None
|
|
src_filter = src_filter or SRC_FILTER_DEFAULT
|
|
src_exts = src_exts or (SRC_BUILD_EXT + SRC_HEADER_EXT)
|
|
return fs.match_src_files(env.subst(src_dir), src_filter, src_exts)
|
|
|
|
|
|
def CollectBuildFiles(
|
|
env, variant_dir, src_dir, src_filter=None, duplicate=False
|
|
): # pylint: disable=too-many-locals
|
|
sources = []
|
|
variants = []
|
|
|
|
src_dir = env.subst(src_dir)
|
|
if src_dir.endswith(os.sep):
|
|
src_dir = src_dir[:-1]
|
|
|
|
for item in env.MatchSourceFiles(src_dir, src_filter, SRC_BUILD_EXT):
|
|
_reldir = os.path.dirname(item)
|
|
_src_dir = os.path.join(src_dir, _reldir) if _reldir else src_dir
|
|
_var_dir = os.path.join(variant_dir, _reldir) if _reldir else variant_dir
|
|
|
|
if _var_dir not in variants:
|
|
variants.append(_var_dir)
|
|
env.VariantDir(_var_dir, _src_dir, duplicate)
|
|
|
|
sources.append(env.File(os.path.join(_var_dir, os.path.basename(item))))
|
|
|
|
middlewares = env.get("__PIO_BUILD_MIDDLEWARES")
|
|
if not middlewares:
|
|
return sources
|
|
|
|
new_sources = []
|
|
for node in sources:
|
|
new_node = node
|
|
for callback, pattern in middlewares:
|
|
if pattern and not fnmatch.fnmatch(node.srcnode().get_path(), pattern):
|
|
continue
|
|
if callback.__code__.co_argcount == 2:
|
|
new_node = callback(env, new_node)
|
|
else:
|
|
new_node = callback(new_node)
|
|
if not new_node:
|
|
break
|
|
if new_node:
|
|
new_sources.append(new_node)
|
|
|
|
return new_sources
|
|
|
|
|
|
def AddBuildMiddleware(env, callback, pattern=None):
|
|
env.Append(__PIO_BUILD_MIDDLEWARES=[(callback, pattern)])
|
|
|
|
|
|
def BuildFrameworks(env, frameworks):
|
|
if not frameworks:
|
|
return
|
|
|
|
if "BOARD" not in env:
|
|
sys.stderr.write(
|
|
"Please specify `board` in `platformio.ini` to use "
|
|
"with '%s' framework\n" % ", ".join(frameworks)
|
|
)
|
|
env.Exit(1)
|
|
|
|
supported_frameworks = env.BoardConfig().get("frameworks", [])
|
|
for name in frameworks:
|
|
if name == "arduino":
|
|
# Arduino IDE appends .o to the end of filename
|
|
Builder.match_splitext = scons_patched_match_splitext
|
|
if "nobuild" not in COMMAND_LINE_TARGETS:
|
|
env.ConvertInoToCpp()
|
|
|
|
if name in supported_frameworks:
|
|
SConscript(env.GetFrameworkScript(name), exports="env")
|
|
else:
|
|
sys.stderr.write("Error: This board doesn't support %s framework!\n" % name)
|
|
env.Exit(1)
|
|
|
|
|
|
def BuildLibrary(env, variant_dir, src_dir, src_filter=None, nodes=None):
|
|
env.ProcessUnFlags(env.get("BUILD_UNFLAGS"))
|
|
nodes = nodes or env.CollectBuildFiles(variant_dir, src_dir, src_filter)
|
|
return env.StaticLibrary(env.subst(variant_dir), nodes)
|
|
|
|
|
|
def BuildSources(env, variant_dir, src_dir, src_filter=None):
|
|
if env.get("PIOMAINPROG"):
|
|
sys.stderr.write(
|
|
"Error: The main program is already constructed and the inline "
|
|
"source files are not allowed. Please use `env.BuildLibrary(...)` "
|
|
"or PRE-type script instead."
|
|
)
|
|
env.Exit(1)
|
|
|
|
nodes = env.CollectBuildFiles(variant_dir, src_dir, src_filter)
|
|
DefaultEnvironment().Append(
|
|
PIOBUILDFILES=[
|
|
env.Object(node) if isinstance(node, FS.File) else node for node in nodes
|
|
]
|
|
)
|
|
|
|
|
|
def exists(_):
|
|
return True
|
|
|
|
|
|
def generate(env):
|
|
env.AddMethod(GetBuildType)
|
|
env.AddMethod(BuildProgram)
|
|
env.AddMethod(ProcessProgramDeps)
|
|
env.AddMethod(ProcessCompileDbToolchainOption)
|
|
env.AddMethod(ProcessProjectDeps)
|
|
env.AddMethod(ParseFlagsExtended)
|
|
env.AddMethod(ProcessFlags)
|
|
env.AddMethod(ProcessUnFlags)
|
|
env.AddMethod(StringifyMacro)
|
|
env.AddMethod(MatchSourceFiles)
|
|
env.AddMethod(CollectBuildFiles)
|
|
env.AddMethod(AddBuildMiddleware)
|
|
env.AddMethod(BuildFrameworks)
|
|
env.AddMethod(BuildLibrary)
|
|
env.AddMethod(BuildSources)
|
|
return env
|