Bazel Cross Compile

3 minute read

Basics of Bazel

  • Target:
    • Bazel 빌드 프로세스의 기본 단위로, 빌드할 수 있는 개별 항목.
  • Label:
    • 특정 Target을 참조하기 위해 사용하는 고유 식별자.
  • Rule:
    • 어떤 액션을 서술하는 규칙.
  • Package:
    • 빌드 파일(BUILD)이 위치한 디렉터리. 소스 코드와 빌드 규칙(rule)을 모아 놓은 논리적 단위.
  • Workspace:
    • Bazel 빌드의 최상위 디렉터리로, 프로젝트의 시작 지점.
  • Starlark:
    • Bazel의 빌드 및 설정 파일을 작성하기 위해 사용하는 내장형 스크립팅 언어.
    • Python과 유사한 문법을 가지고 있지만, 빌드 시스템에 최적화되어 있고, 결정적(deterministic)으로 동작하도록 설계.

Getting Started with Cross Compilation in Bazel

크로스 컴파일러 준비

타겟 플랫폼을 위한 알맞은 툴체인 준비

  • 툴체인(Toolchain)이란?
    • 타겟 플랫폼(예: CPU 아키텍처나 운영체제)이 현재 개발 중인 호스트 플랫폼과 다를 때, 타겟 환경에 맞는 실행 파일을 생성하기 위한 도구들의 모음을 의미.

Platform

Bazel에서 Platform은 특정 빌드 환경에 맞는 하드웨어 및 소프트웨어 요구 사항을 정의하는 개념입니다. 플랫폼을 정의함으로써 Bazel은 여러 환경에서 빌드 작업을 더 효율적으로 관리할 수 있으며, 각 플랫폼에 맞는 툴체인을 자동으로 선택하여 빌드를 수행할 수 있습니다.

Bazel에서 Platform은 다음 두 가지 기본 요소를 사용해 정의합니다.

  • 제약 조건 설정 (constraint_setting)
    • Platform에서 사용할 수 있는 제약 조건의 유형을 정의합니다.
    • 예를 들어, 운영체제 유형, CPU 아키텍처, C++ ABI 등이 포함될 수 있습니다.
  • 제약 조건 값 (constraint_value)
    • 각 제약 조건에 대해 구체적인 값을 지정합니다.
    • 예를 들어, 운영체제 유형에 대한 제약 조건으로 linux, windows, macos 등의 값을 정의할 수 있습니다.
constraint_value(
    name = "bare_metal",
    constraint_setting = "@platforms//os",
    visibility = ["//visibility:public"],
)

platform(
    name = "riscv64_bare_metal",
    constraint_values = [
        ":bare_metal",
        "@platforms//cpu:riscv64",
    ],
)

Toolchain

Bazel에서 크로스 컴파일을 수행하려면 컴파일 타겟에 맞는 도구 체인(toolchain)을 설정해야 합니다. 이는 CPU 아키텍처나 운영체제에 따라 달라지므로, 보통 CROSSTOOL 파일이나 .bazelrc 파일을 사용하여 설정합니다. Bazel의 기본 툴체인을 그대로 사용하거나, 새로운 툴체인을 지정할 수 있습니다.

  • 커스텀 룰을 새로 작성하면서 툴체인을 정의하는 방법
  • 기존의 룰을 재사용하여 툴체인을 정의하는 방법

여기서는 기존의 룰을 재사용하여 툴체인을 정의하는 방법을 설명합니다.

Toolchain config 정의하기

toolchain_config는 Bazel에서 특정 툴체인(toolchain)의 동작을 구성하고 설정하는 역할을 하는 구성 파일. 이 설정은 컴파일러와 링커가 특정 작업을 수행할 때 어떤 플래그를 사용할지, 어떤 경로에서 실행 파일을 찾을지 등 세부 사항을 다룹니다.

load("@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl", "feature", "tool_path", ... )

toolchain_tools = ["gcc", "ld", "ar", "cpp", "gcov", "nm", "objdump", "strip"]
tool_paths = [tool_path(name = tool, path = "/usr/bin/riscv64-linux-gnu-{}".format(tool)) for tool in toolchain_tools]

feature란?

  • Bazel에서 feature 규칙은 특정 컴파일/빌드 설정을 정의하여 빌드 프로세스 중에 필요한 플래그나 동작을 제어하는 데 사용됩니다.
# rv64_bare_metal_toolchain.bzl
load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES")

ASSEMBLE_ACTIONS = [
      ACTION_NAMES.assemble,
      ACTION_NAMES.preprocess_assemble,
  ]

COMPILE_ACTIONS = [
    ACTION_NAMES.cpp_compile,
    ACTION_NAMES.c_compile
]

ASSEMBLE_AND_COMPILE_ACTIONS = ASSEMBLE_ACTIONS + COMPILE_ACTIONS

arch_abi = feature(
    name = "arch_and_abi",
    enabled = True,
    flag_sets = [
        flag_set(
            actions = ASSEMBLE_AND_COMPILE_ACTIONS,
            flag_groups = [
                flag_group(
                    flags = ["-march=rv64i", "-mabi=lp64"],
                ),
            ],
        )
    ],
)

...

features = [arch_abi, link_static_no_stdlib, no_build_id]

...

return cc_common.create_cc_toolchain_config_info(
    ctx = ctx,
    toolchain_identifier = "local",
    host_system_name = "local",
    target_system_name = "local",
    target_cpu = "riscv64",
    target_libc = "unknown",
    compiler = "clang",
    abi_version = "unknown",
    abi_libc_version = "unknown",
    tool_paths = tool_paths,
    cxx_builtin_include_directories = [
        "/usr/riscv64-linux-gnu/include/",
        "/usr/lib/gcc-cross/riscv64-linux-gnu/10/include/",
    ],
    features = features,
)

CcToolchainConfigInfo?

  • CcToolchainConfigInfo는 Bazel의 C/C++ 빌드에서 툴체인의 구성을 나타내는 제공자(Provider).
  • 이 제공자는 Bazel이 C/C++ 빌드의 특정 요구 사항을 충족하기 위해 툴체인에 대한 세부 설정 정보를 전달하고 관리하는 데 사용.
rv64_bare_metal_toolchain_config = rule(
  implementation = _impl,
  attrs = {},
  provides = [CcToolchainConfigInfo],
)
load(":rv64_bare_metal_toolchain.bzl", "rv64_bare_metal_toolchain_config")

rv64_bare_metal_toolchain_config(
  name = "rv64_bare_metal_config"
);

Toolchain 정의하기

load(":rv64_bare_metal_toolchain.bzl", "rv64_bare_metal_toolchain_config")

rv64_bare_metal_toolchain_config(
  name = "rv64_bare_metal_config"
);

cc_toolchain(
  name = "rv64_bare_metal_toolchain",
  toolchain_identifier = "local",
  toolchain_config = ":rv64_bare_metal_config",
  ...
)

toolchain(
  name = "riscv64_bare_metal_toolchain_from_linux_x86_64",
  exec_compatible_with = [
    "@platforms//os:linux",
    "@platforms//cpu:x86_64",
  ],
  target_compatible_with = [
    "//platform:bare_metal",
    "@platforms//cpu:riscv64",
  ],
  toolchain = ":rv64_bare_metal_toolchain",
  toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
)

Toolchain 사용하기

이제 일반적인 cc_library와 cc_binary 타겟을 작성할 수 있습니다!

BARE_METAL_RISCV64_CONSTRAINTS = [
  "//platform:bare_metal",
  "@platforms//cpu:riscv64",
]

cc_binary(
  name = "program",
  srcs = [
    "program.c",
    "boot.S",
  ],
  additional_linker_inputs = [
    "link_script.ld",
  ],
  linkopts = ["-Wl,-T $(location :link_script.ld)"],
  target_compatible_with = BARE_METAL_RISCV64_CONSTRAINTS,
)

빌드 명령어

크로스 컴파일을 실행할 때는 –platforms 플래그를 사용하여 타겟 플랫폼을 지정합니다. 예를 들어: bazel build -s –platforms=//platform:riscv64_bare_metal //program

참조

Cross compiling C and C++ with Bazel, Uros Popovic

Tags:

Categories:

Updated:

Leave a comment