Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 32 additions & 41 deletions build-system/cpp-cmake-conan/src/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,45 +12,23 @@
#
# SPDX-License-Identifier: Apache-2.0

import json
import os
import subprocess
from argparse import ArgumentParser
from pathlib import Path
from typing import List

from velocitas_lib import get_valid_arch, get_workspace_dir
from utils import (
get_build_folder,
get_build_tools_path,
load_toolchain,
safe_get_workspace_dir,
)
from velocitas_lib import get_valid_arch

CMAKE_EXECUTABLE = "cmake"
CONAN_EXECUTABLE = "conan"


def safe_get_workspace_dir() -> str:
"""A safe version of get_workspace_dir which defaults to '.'."""
try:
return get_workspace_dir()
except Exception:
return os.path.abspath(".")


def get_build_folder(build_arch: str, host_arch: str):
if host_arch == build_arch:
return os.path.join(safe_get_workspace_dir(), "build")
return os.path.join(safe_get_workspace_dir(), f"build_linux_{host_arch}")


def get_build_tools_path(build_folder_path: str) -> str:
paths: List[str] = []
with open(
os.path.join(build_folder_path, "conanbuildinfo.txt"), encoding="utf-8"
) as file:
for line in file:
if line.startswith("PATH="):
path_list = json.loads(line[len("PATH=") :])
paths.extend(path_list)
return ";".join(paths)


def print_build_info(
build_variant: str,
build_arch: str,
Expand Down Expand Up @@ -88,23 +66,31 @@ def build(
host_arch: str,
build_target: str,
static_build: bool,
toolchain_file: str = "",
coverage: bool = True,
) -> None:
cxx_flags = ["-g"]
if coverage:
cxx_flags.append("--coverage")

if build_variant == "release":
cxx_flags.append("-s")
cxx_flags.append("-O3")
xcompile_toolchain_file = ""
if toolchain_file != "":
load_toolchain(toolchain_file)
xcompile_toolchain_file = f"-DCMAKE_TOOLCHAIN_FILE={os.path.join(safe_get_workspace_dir(),'OEToolchainConfig.cmake')}"
cmake_cxx_flags = f"-DCMAKE_CXX_FLAGS={os.environ.get('CXXFLAGS', '')}"
else:
cxx_flags.append("-O0")
cxx_flags = ["-g"]
if coverage:
cxx_flags.append("--coverage")

if build_variant == "release":
cxx_flags.append("-s")
cxx_flags.append("-O3")
else:
cxx_flags.append("-O0")

cmake_cxx_flags = f"-DCMAKE_CXX_FLAGS={' '.join(cxx_flags)}"

build_folder = get_build_folder(build_arch, host_arch)
os.makedirs(build_folder, exist_ok=True)

xcompile_toolchain_file = ""
if build_arch != host_arch:
if build_arch != host_arch and xcompile_toolchain_file == "":
profile_build_path = (
Path(__file__)
.absolute()
Expand All @@ -126,7 +112,7 @@ def build(
"-B.",
"-G",
"Ninja",
f"-DCMAKE_CXX_FLAGS={' '.join(cxx_flags)}",
cmake_cxx_flags,
],
cwd=build_folder,
)
Expand Down Expand Up @@ -172,6 +158,11 @@ def cli() -> None:
parser.add_argument(
"-s", "--static", action="store_true", help="Links all dependencies statically."
)
parser.add_argument(
"--toolchain",
default="",
help="Specify a file (absolute path) containing the definitions of a custom toolchain.",
)
parser.add_argument(
"-x",
"--cross",
Expand All @@ -193,7 +184,7 @@ def cli() -> None:
host_arch = get_valid_arch(host_arch)

print_build_info(args.variant, build_arch, host_arch, args.target, args.static)
build(args.variant, build_arch, host_arch, args.target, args.static)
build(args.variant, build_arch, host_arch, args.target, args.static, args.toolchain)


if __name__ == "__main__":
Expand Down
114 changes: 70 additions & 44 deletions build-system/cpp-cmake-conan/src/install_deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,8 @@
from argparse import ArgumentParser
from pathlib import Path

from velocitas_lib import get_valid_arch, get_workspace_dir


def safe_get_workspace_dir() -> str:
"""A safe version of get_workspace_dir which defaults to '.'."""
try:
return get_workspace_dir()
except Exception:
return os.path.abspath(".")


def get_build_folder(build_arch: str, host_arch: str):
if host_arch == build_arch:
return os.path.join(safe_get_workspace_dir(), "build")
return os.path.join(safe_get_workspace_dir(), f"build_linux_{host_arch}")
from utils import get_build_folder, load_toolchain, safe_get_workspace_dir
from velocitas_lib import get_valid_arch


def get_profile_name(arch: str, build_variant: str) -> str:
Expand All @@ -53,11 +40,31 @@ def get_profile_name(arch: str, build_variant: str) -> str:
return f"linux_{get_valid_arch(arch)}_{build_variant}"


def get_valid_conan_arch(arch: str) -> str:
"""Return the valid architecture for the given `arch`.

Args:
arch (str): The architecture to validate.

Returns:
str: The valid architecture.
"""
if arch == "x86_64":
return "x86_64"
elif arch == "aarch64" or arch == "armv8" or arch == "arm64":
return "armv8"
elif arch == "armv7":
return "armv7"
else:
raise ValueError(f"Unsupported architecture: {arch}")


def install_deps_via_conan(
build_arch: str,
host_arch: str,
is_debug: bool = False,
build_all_deps: bool = False,
toolchain_file: str = "",
) -> None:
build_variant = "debug" if is_debug else "release"

Expand All @@ -68,42 +75,52 @@ def install_deps_via_conan(
".conan", "profiles", get_profile_name(build_arch, build_variant)
)
)

profile_host_path = (
Path(__file__)
.absolute()
.parent.joinpath(
".conan", "profiles", get_profile_name(host_arch, build_variant)
if toolchain_file != "":
load_toolchain(toolchain_file)
host_config = [
"-s:h",
f"arch={get_valid_conan_arch(os.environ.get('OECORE_TARGET_ARCH','').strip())}",
"-s:h",
f"arch_build={get_valid_conan_arch(build_arch)}",
]
else:
profile_host_path = (
Path(__file__)
.absolute()
.parent.joinpath(
".conan", "profiles", get_profile_name(host_arch, build_variant)
)
)
)
host_config = ["-pr:h", f"{profile_host_path}"]

build_folder = get_build_folder(build_arch, host_arch)
os.makedirs(build_folder, exist_ok=True)

deps_to_build = "missing" if not build_all_deps else "*"

toolchain = f"/usr/bin/{host_arch}-linux-gnu"
build_host = f"{host_arch}-linux-gnu"
cc_compiler = "gcc"
cxx_compiler = "g++"

os.environ["CONAN_CMAKE_FIND_ROOT_PATH"] = toolchain
os.environ["CONAN_CMAKE_SYSROOT"] = toolchain
os.environ["CC"] = f"{build_host}-{cc_compiler}"
os.environ["CXX"] = f"{build_host}-{cxx_compiler}"

if toolchain_file == "":
toolchain = f"/usr/bin/{host_arch}-linux-gnu"
build_host = f"{host_arch}-linux-gnu"
cc_compiler = "gcc"
cxx_compiler = "g++"

os.environ["CONAN_CMAKE_FIND_ROOT_PATH"] = toolchain
os.environ["CONAN_CMAKE_SYSROOT"] = toolchain
os.environ["CC"] = f"{build_host}-{cc_compiler}"
os.environ["CXX"] = f"{build_host}-{cxx_compiler}"

args = [
"conan",
"install",
*host_config,
"-pr:b",
str(profile_build_path),
"--build",
deps_to_build,
safe_get_workspace_dir(),
]
subprocess.check_call(
[
"conan",
"install",
"-pr:h",
profile_host_path,
"-pr:b",
profile_build_path,
"--build",
deps_to_build,
safe_get_workspace_dir(),
],
args,
env=os.environ,
cwd=build_folder,
)
Expand All @@ -129,6 +146,11 @@ def cli() -> None:
action="store_true",
help="Forces all dependencies to be rebuild from source.",
)
argument_parser.add_argument(
"--toolchain",
default="",
help="Specify a file (absolute path) containing the definitions of a custom toolchain.",
)
argument_parser.add_argument(
"-x",
"--cross",
Expand All @@ -149,7 +171,11 @@ def cli() -> None:
subprocess.check_call(["conan", "config", "set", "general.revisions_enabled=1"])

install_deps_via_conan(
build_arch, host_arch, args.debug and not args.release, args.build_all_deps
build_arch,
host_arch,
args.debug and not args.release,
args.build_all_deps,
args.toolchain,
)


Expand Down
63 changes: 63 additions & 0 deletions build-system/cpp-cmake-conan/src/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Copyright (c) 2024 Contributors to the Eclipse Foundation
#
# This program and the accompanying materials are made available under the
# terms of the Apache License, Version 2.0 which is available at
# https://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.
#
# SPDX-License-Identifier: Apache-2.0

import json
import os
import subprocess
from typing import List

from velocitas_lib import get_workspace_dir


def safe_get_workspace_dir() -> str:
"""A safe version of get_workspace_dir which defaults to '.'."""
try:
return get_workspace_dir().strip()
except Exception:
return os.path.abspath(".")


def get_build_folder(build_arch: str, host_arch: str):
if host_arch == build_arch:
return os.path.join(safe_get_workspace_dir(), "build")
return os.path.join(safe_get_workspace_dir(), f"build_linux_{host_arch}")


def get_build_tools_path(build_folder_path: str) -> str:
paths: List[str] = []
with open(
os.path.join(build_folder_path, "conanbuildinfo.txt"), encoding="utf-8"
) as file:
for line in file:
if line.startswith("PATH="):
path_list = json.loads(line[len("PATH=") :])
paths.extend(path_list)
return ";".join(paths)


def load_toolchain(toolchain_file: str) -> None:
if not os.path.exists(toolchain_file):
raise FileNotFoundError(f"Toolchain file {toolchain_file} not found.")
print(f"Loading toolchain file {toolchain_file}")

proc = subprocess.Popen(
["env", "-i", "bash", "-c", f"source {toolchain_file} && env"],
stdout=subprocess.PIPE,
)
if proc.stdout is not None:
for line in proc.stdout:
(key, _, value) = line.decode().partition("=")
print(f"Setting {key} to {value}")
os.environ[key] = value
proc.communicate()