Skip to content

Commit 4f3243f

Browse files
authored
Move CMake code for building multilib and variants to subprojects (#562)
This patch restructures the top-level CMake script in order to split building the library variants into a separate CMake project. This better encapsulates the code to build the variants, and untethers it from the toolchain builds so that libraries can be developed and tested without having the rebuild the entire toolchain. Similarly, a new subproject has been created to build a multilib set of library variants, using a selected C library. A simple JSON format has been used to define the flags and variants, which are then built and collected into the appropriate layout.
1 parent 325434a commit 4f3243f

File tree

74 files changed

+3802
-1673
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+3802
-1673
lines changed

CMakeLists.txt

Lines changed: 99 additions & 1547 deletions
Large diffs are not rendered by default.

arm-multilib/CMakeLists.txt

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
#
2+
# Copyright (c) 2024, Arm Limited and affiliates.
3+
# SPDX-License-Identifier: Apache-2.0
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
18+
# CMake build for a multilib layout of library variants, with each
19+
# variant in a subdirectory and a multilib.yaml file to map flags to
20+
# a variant.
21+
22+
cmake_minimum_required(VERSION 3.20)
23+
24+
project(arm-multilib)
25+
26+
# Root directory of the repo.
27+
set(TOOLCHAIN_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..)
28+
29+
# Cache variables to be set by user
30+
set(MULTILIB_JSON "" CACHE STRING "JSON file to load library definitions from.")
31+
set(ENABLE_VARIANTS "all" CACHE STRING "Semicolon separated list of variants to build, or \"all\". Must match entries in the json.")
32+
set(C_LIBRARY "picolibc" CACHE STRING "Which C library to use.")
33+
set_property(CACHE C_LIBRARY PROPERTY STRINGS picolibc newlib llvmlibc)
34+
set(LLVM_BINARY_DIR "" CACHE PATH "Path to LLVM toolchain build or install root.")
35+
set(LIBC_HDRGEN "" CACHE PATH "Path to prebuilt lbc-hdrgen if not included in LLVM binaries set by LLVM_BINARY_DIR")
36+
set(
37+
FVP_INSTALL_DIR
38+
"" CACHE STRING
39+
"The directory in which the FVP models are installed. These are not
40+
included in this repository, but can be downloaded by the script
41+
fvp/get_fvps.sh"
42+
)
43+
set(FVP_CONFIG_DIR "${TOOLCHAIN_SOURCE_DIR}/fvp/config" CACHE STRING "The directory in which the FVP models are installed.")
44+
45+
# If a compiler launcher such as ccache has been set, it should be
46+
# passed down to each subproject build.
47+
set(compiler_launcher_cmake_args "")
48+
if(CMAKE_C_COMPILER_LAUNCHER)
49+
list(APPEND compiler_launcher_cmake_args "-DCMAKE_C_COMPILER_LAUNCHER=${CMAKE_C_COMPILER_LAUNCHER}")
50+
endif()
51+
if(CMAKE_CXX_COMPILER_LAUNCHER)
52+
list(APPEND compiler_launcher_cmake_args "-DCMAKE_CXX_COMPILER_LAUNCHER=${CMAKE_CXX_COMPILER_LAUNCHER}")
53+
endif()
54+
55+
# Arguments to pass down to the library projects.
56+
foreach(arg
57+
LLVM_BINARY_DIR
58+
LIBC_HDRGEN
59+
FVP_INSTALL_DIR
60+
FVP_CONFIG_DIR
61+
)
62+
if(${arg})
63+
list(APPEND passthrough_dirs "-D${arg}=${${arg}}")
64+
endif()
65+
endforeach()
66+
67+
include(ExternalProject)
68+
include(${TOOLCHAIN_SOURCE_DIR}/cmake/fetch_llvm.cmake)
69+
list(APPEND passthrough_dirs "-DFETCHCONTENT_SOURCE_DIR_LLVMPROJECT=${FETCHCONTENT_SOURCE_DIR_LLVMPROJECT}")
70+
if(C_LIBRARY STREQUAL picolibc)
71+
include(${TOOLCHAIN_SOURCE_DIR}/cmake/fetch_picolibc.cmake)
72+
list(APPEND passthrough_dirs "-DFETCHCONTENT_SOURCE_DIR_PICOLIBC=${FETCHCONTENT_SOURCE_DIR_PICOLIBC}")
73+
elseif(C_LIBRARY STREQUAL newlib)
74+
include(${TOOLCHAIN_SOURCE_DIR}/cmake/fetch_newlib.cmake)
75+
list(APPEND passthrough_dirs "-DFETCHCONTENT_SOURCE_DIR_NEWLIB=${FETCHCONTENT_SOURCE_DIR_NEWLIB}")
76+
endif()
77+
78+
# Target for any dependencies to build the runtimes project.
79+
add_custom_target(runtimes-depends)
80+
81+
# If building llvm-libc, ensure libc-hdrgen is available.
82+
if(C_LIBRARY STREQUAL llvmlibc)
83+
if(NOT LIBC_HDRGEN)
84+
if(EXISTS ${LLVM_BINARY_DIR}/bin/libc-hdrgen${CMAKE_EXECUTABLE_SUFFIX})
85+
set(LIBC_HDRGEN ${LLVM_BINARY_DIR}/bin/libc-hdrgen${CMAKE_EXECUTABLE_SUFFIX})
86+
else()
87+
ExternalProject_Add(
88+
libc_hdrgen
89+
SOURCE_DIR ${llvmproject_SOURCE_DIR}/llvm
90+
CMAKE_ARGS
91+
-DLLVM_ENABLE_RUNTIMES=libc
92+
-DLLVM_LIBC_FULL_BUILD=ON
93+
-DCMAKE_BUILD_TYPE=Debug
94+
BUILD_COMMAND ${CMAKE_COMMAND} --build . --target libc-hdrgen
95+
INSTALL_COMMAND ${CMAKE_COMMAND} -E true
96+
CONFIGURE_HANDLED_BY_BUILD TRUE
97+
)
98+
ExternalProject_Get_property(libc_hdrgen BINARY_DIR)
99+
set(LIBC_HDRGEN ${BINARY_DIR}/bin/libc-hdrgen${CMAKE_EXECUTABLE_SUFFIX})
100+
add_dependencies(runtimes-depends libc_hdrgen)
101+
endif()
102+
endif()
103+
list(APPEND passthrough_dirs "-DLIBC_HDRGEN=${LIBC_HDRGEN}")
104+
endif()
105+
106+
# Create one target to run all the tests.
107+
add_custom_target(check-${C_LIBRARY})
108+
add_custom_target(check-compiler-rt)
109+
add_custom_target(check-cxx)
110+
add_custom_target(check-cxxabi)
111+
add_custom_target(check-unwind)
112+
113+
add_custom_target(check-all)
114+
add_dependencies(
115+
check-all
116+
check-${C_LIBRARY}
117+
check-compiler-rt
118+
check-cxx
119+
check-cxxabi
120+
check-unwind
121+
)
122+
123+
# Read the JSON file to load a multilib configuration.
124+
file(READ ${MULTILIB_JSON} multilib_json_str)
125+
string(JSON multilib_defs GET ${multilib_json_str} "libs")
126+
127+
string(JSON lib_count LENGTH ${multilib_defs})
128+
math(EXPR lib_count_dec "${lib_count} - 1")
129+
130+
foreach(lib_idx RANGE ${lib_count_dec})
131+
string(JSON lib_def GET ${multilib_defs} ${lib_idx})
132+
string(JSON variant GET ${lib_def} "variant")
133+
134+
if(variant IN_LIST ENABLE_VARIANTS OR ENABLE_VARIANTS STREQUAL "all")
135+
string(JSON variant_multilib_flags GET ${lib_def} "flags")
136+
# Placeholder libraries won't have a json, so store the error in
137+
# a variable so a fatal error isn't generated.
138+
string(JSON variant_json ERROR_VARIABLE json_error GET ${lib_def} "json")
139+
140+
if(NOT variant_json STREQUAL "json-NOTFOUND")
141+
# Sort by target triple
142+
if(variant MATCHES "^aarch64")
143+
set(parent_dir_name aarch64-none-elf)
144+
else()
145+
set(parent_dir_name arm-none-eabi)
146+
endif()
147+
set(destination_directory "${CMAKE_CURRENT_BINARY_DIR}/multilib/${parent_dir_name}/${variant}")
148+
install(
149+
DIRECTORY ${destination_directory}
150+
DESTINATION ${parent_dir_name}
151+
)
152+
set(variant_json_file ${CMAKE_CURRENT_SOURCE_DIR}/json/variants/${variant_json})
153+
154+
ExternalProject_Add(
155+
runtimes-${variant}
156+
PREFIX ${CMAKE_BINARY_DIR}/lib-builds
157+
SOURCE_DIR ${TOOLCHAIN_SOURCE_DIR}/arm-runtimes
158+
INSTALL_DIR ${destination_directory}
159+
DEPENDS runtimes-depends
160+
CMAKE_ARGS
161+
${compiler_launcher_cmake_args}
162+
${passthrough_dirs}
163+
-DVARIANT_JSON=${variant_json_file}
164+
-DC_LIBRARY=${C_LIBRARY}
165+
-DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
166+
STEP_TARGETS build install
167+
USES_TERMINAL_CONFIGURE FALSE
168+
USES_TERMINAL_BUILD TRUE
169+
USES_TERMINAL_TEST TRUE
170+
LIST_SEPARATOR ,
171+
CONFIGURE_HANDLED_BY_BUILD TRUE
172+
TEST_EXCLUDE_FROM_MAIN TRUE
173+
)
174+
175+
# Read info from the variant specific json.
176+
# From the json, check which tests are enabled.
177+
file(READ ${variant_json_file} variant_json_str)
178+
foreach(test_enable_var
179+
ENABLE_LIBC_TESTS
180+
ENABLE_COMPILER_RT_TESTS
181+
ENABLE_LIBCXX_TESTS
182+
)
183+
string(JSON read_${test_enable_var} ERROR_VARIABLE json_error GET ${variant_json_str} "args" ${C_LIBRARY} ${test_enable_var})
184+
if(read_${test_enable_var} STREQUAL "json-NOTFOUND")
185+
string(JSON read_${test_enable_var} ERROR_VARIABLE json_error GET ${variant_json_str} "args" "common" ${test_enable_var})
186+
if(read_${test_enable_var} STREQUAL "json-NOTFOUND")
187+
set(read_${test_enable_var} "OFF")
188+
endif()
189+
endif()
190+
endforeach()
191+
set(check_targets "")
192+
if(read_ENABLE_LIBC_TESTS)
193+
list(APPEND check_targets check-${C_LIBRARY})
194+
endif()
195+
if(read_ENABLE_COMPILER_RT_TESTS)
196+
list(APPEND check_targets check-compiler-rt)
197+
endif()
198+
if(read_ENABLE_LIBCXX_TESTS)
199+
list(APPEND check_targets check-cxx)
200+
list(APPEND check_targets check-cxxabi)
201+
list(APPEND check_targets check-unwind)
202+
endif()
203+
foreach(check_target ${check_targets})
204+
ExternalProject_Add_Step(
205+
runtimes-${variant}
206+
${check_target}
207+
COMMAND "${CMAKE_COMMAND}" --build <BINARY_DIR> --target ${check_target}
208+
USES_TERMINAL TRUE
209+
EXCLUDE_FROM_MAIN TRUE
210+
ALWAYS TRUE
211+
)
212+
ExternalProject_Add_StepTargets(runtimes-${variant} ${check_target})
213+
ExternalProject_Add_StepDependencies(
214+
runtimes-${variant}
215+
${check_target}
216+
runtimes-${variant}-build
217+
)
218+
add_custom_target(${check_target}-${variant})
219+
add_dependencies(${check_target} runtimes-${variant}-${check_target})
220+
add_dependencies(${check_target}-${variant} runtimes-${variant}-${check_target})
221+
endforeach()
222+
223+
# Add the variant to the multilib yaml
224+
string(APPEND multilib_yaml_content "- Dir: ${parent_dir_name}/${variant}\n")
225+
string(APPEND multilib_yaml_content " Flags:\n")
226+
string(REPLACE " " ";" multilib_flags_list ${variant_multilib_flags})
227+
foreach(flag ${multilib_flags_list})
228+
string(APPEND multilib_yaml_content " - ${flag}\n")
229+
endforeach()
230+
string(APPEND multilib_yaml_content " Group: stdlibs\n")
231+
else()
232+
# In place of a json, an error message is expected.
233+
string(JSON variant_error_msg GET ${lib_def} "error")
234+
235+
string(APPEND multilib_yaml_content "- Error: \"${variant_error_msg}\"\n")
236+
string(APPEND multilib_yaml_content " Flags:\n")
237+
string(REPLACE " " ";" multilib_flags_list ${variant_multilib_flags})
238+
foreach(flag ${multilib_flags_list})
239+
string(APPEND multilib_yaml_content " - ${flag}\n")
240+
endforeach()
241+
string(APPEND multilib_yaml_content " Group: stdlibs\n")
242+
endif()
243+
endif()
244+
245+
endforeach()
246+
247+
# Multilib file is generated in two parts.
248+
# 1. Template is filled with multilib flags from json
249+
configure_file(
250+
${CMAKE_CURRENT_SOURCE_DIR}/multilib.yaml.in
251+
${CMAKE_CURRENT_BINARY_DIR}/multilib-without-fpus.yaml
252+
@ONLY
253+
)
254+
255+
# 2. multilib-generate.py maps compiler command line options to flags
256+
add_custom_command(
257+
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/multilib-fpus.yaml
258+
COMMAND ${Python3_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/multilib-generate.py"
259+
"--clang=${LLVM_BINARY_DIR}/bin/clang${CMAKE_EXECUTABLE_SUFFIX}"
260+
"--llvm-source=${FETCHCONTENT_SOURCE_DIR_LLVMPROJECT}"
261+
>> ${CMAKE_CURRENT_BINARY_DIR}/multilib-fpus.yaml
262+
)
263+
264+
# Combine the two parts.
265+
add_custom_command(
266+
OUTPUT
267+
${CMAKE_CURRENT_BINARY_DIR}/multilib/multilib.yaml
268+
COMMAND
269+
${CMAKE_COMMAND} -E cat
270+
${CMAKE_CURRENT_BINARY_DIR}/multilib-without-fpus.yaml
271+
${CMAKE_CURRENT_BINARY_DIR}/multilib-fpus.yaml
272+
> ${CMAKE_CURRENT_BINARY_DIR}/multilib/multilib.yaml
273+
DEPENDS
274+
${CMAKE_CURRENT_BINARY_DIR}/multilib-without-fpus.yaml
275+
${CMAKE_CURRENT_BINARY_DIR}/multilib-fpus.yaml
276+
)
277+
278+
add_custom_target(multilib-yaml ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/multilib/multilib.yaml)
279+
install(
280+
FILES ${CMAKE_CURRENT_BINARY_DIR}/multilib/multilib.yaml
281+
DESTINATION .
282+
)

0 commit comments

Comments
 (0)