Skip to content

Commit 01bfe79

Browse files
committed
Rewrite ldc2.conf cmake generation
When building, always generate separate files for each ldc2.conf section. This allows creating a full ldc2.conf, part by part, without carrying about the statements' order in the cmake file. When installing, `cat` all the configured files and install it as a single ldc2.conf file. Added the CONF_PREF_DIR cmake option for installing the conf files as a directory, without concatenating them. This is useful when building the runtime as a separate project as this setting would allow generating a (partial) ldc2.conf, enough to support the runtime libraries, which could latter be merged with the config generated by the root ldc project to get a fully functional ldc2. This is a requirement when cross-compiling and installing the libraries on another device or when performing multilib builds outside of -DMULTILIB=ON Also add a more detailed documentation about the configuration. Signed-off-by: Andrei Horodniceanu <a.horodniceanu@proton.me>
1 parent 3652f71 commit 01bfe79

File tree

8 files changed

+642
-159
lines changed

8 files changed

+642
-159
lines changed

.github/actions/merge-macos/action.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ runs:
4141
# ldc2.conf:
4242
# 1) make a backup copy
4343
cp etc/ldc2.conf /tmp/ldc2.conf.bak
44-
# 2) strip to the header comments (remove all existing sections, only keep `default:` line)
45-
sed -i '' '/^default:$/q' etc/ldc2.conf
44+
# 2) strip to the header comments (remove all existing sections, only keep `default:` or `"default":` line)
45+
sed -i '' '/^"\?default"\?:$/q' etc/ldc2.conf
4646
# 3) append all sections (except for wasm)
4747
cat >>etc/ldc2.conf <<EOF
4848
{

CMakeLists.txt

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,16 @@ endif()
1616
project(ldc)
1717

1818
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules")
19+
set(LDC_SOURCE_ROOT "${CMAKE_SOURCE_DIR}")
20+
21+
include(LdcCommon)
1922

2023
include(FindDCompiler)
2124
include(CheckCXXCompilerFlag)
2225
include(CheckDSourceCompiles)
2326
include(CheckLinkFlag)
2427
include(BuildDExecutable)
2528

26-
# Helper function
27-
function(append value)
28-
foreach(variable ${ARGN})
29-
if(${variable} STREQUAL "")
30-
set(${variable} "${value}" PARENT_SCOPE)
31-
else()
32-
set(${variable} "${${variable}} ${value}" PARENT_SCOPE)
33-
endif()
34-
endforeach(variable)
35-
endfunction()
36-
3729
#
3830
# Locate LLVM.
3931
#
@@ -128,21 +120,9 @@ set(DMDFE_PATCH_VERSION 0)
128120

129121
set(DMD_VERSION ${DMDFE_MAJOR_VERSION}.${DMDFE_MINOR_VERSION}.${DMDFE_PATCH_VERSION})
130122

131-
# Generally, we want to install everything into CMAKE_INSTALL_PREFIX, but when
132-
# it is /usr, put the config files into /etc to meet common practice.
133-
if(NOT DEFINED SYSCONF_INSTALL_DIR)
134-
if(CMAKE_INSTALL_PREFIX STREQUAL "/usr")
135-
set(SYSCONF_INSTALL_DIR "/etc")
136-
else()
137-
set(SYSCONF_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/etc")
138-
endif()
139-
endif()
140-
141123
set(D_VERSION ${DMDFE_MAJOR_VERSION} CACHE STRING "D language version")
142124
set(PROGRAM_PREFIX "" CACHE STRING "Prepended to ldc/ldmd binary names")
143125
set(PROGRAM_SUFFIX "" CACHE STRING "Appended to ldc/ldmd binary names")
144-
set(CONF_INST_DIR ${SYSCONF_INSTALL_DIR} CACHE PATH "Directory ldc2.conf is installed to")
145-
set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/include/d CACHE PATH "Path to install D modules to")
146126

147127
# Note: LIB_SUFFIX should perhaps be renamed to LDC_LIBDIR_SUFFIX.
148128
set(LIB_SUFFIX "" CACHE STRING "Appended to the library installation directory. Set to '64' to install libraries into ${PREFIX}/lib64.")
@@ -925,7 +905,7 @@ if(NOT DEFINED COMPILER_RT_LIBDIR_CONFIG)
925905
endif()
926906
if(DEFINED COMPILER_RT_LIBDIR_CONFIG)
927907
message(STATUS "Adding ${COMPILER_RT_LIBDIR_CONFIG} to lib-dirs in configuration file")
928-
set(OPTIONAL_COMPILER_RT_DIR "\n \"${COMPILER_RT_LIBDIR_CONFIG}\", // compiler-rt directory")
908+
makeConfSection(NAME "35-ldc-compiler-rt" SECTION "default" LIB_DIRS "${COMPILER_RT_LIBDIR}")
929909
endif()
930910

931911
#
@@ -1006,6 +986,15 @@ option(LDC_BUILD_RUNTIME "Build the runtime libraries" ${_LDC_BUILD_RUNTIME_DEFA
1006986

1007987
if(LDC_BUILD_RUNTIME)
1008988
add_subdirectory(runtime)
989+
990+
# POST_BUILD can't be used for targets in a different directory so
991+
# runtime/CMakeLists.txt can't directly add the commands.
992+
if(_LDC_POST_BUILD_COMMANDS)
993+
add_custom_command(TARGET ${LDC_EXE} POST_BUILD
994+
${_LDC_POST_BUILD_COMMANDS}
995+
BYPRODUCTS ${_LDC_POST_BUILD_BYPRODUCTS}
996+
)
997+
endif()
1009998
else()
1010999
message(STATUS "NOT building the runtime libraries (LDC_BUILD_RUNTIME=OFF)")
10111000
endif()
@@ -1045,6 +1034,21 @@ if(${BUILD_SHARED})
10451034
install(TARGETS ${LDC_LIB} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX})
10461035
endif()
10471036

1037+
# Add some documentation to the installed config files
1038+
if(NOT DONT_INSTALL_CONF)
1039+
if(CONF_PREFER_DIR)
1040+
install(FILES "ldc2.conf.skel" DESTINATION "${CONF_INST_DIR}/ldc2.conf" RENAME 00-example.conf)
1041+
else()
1042+
# TODO: Replace this with COPY_FILE when cmake_mininum_required is 3.21
1043+
file(COPY "ldc2.conf.skel" DESTINATION "${LDC2_INSTALL_CONF}")
1044+
file(RENAME "${LDC2_INSTALL_CONF}/ldc2.conf.skel" "${LDC2_INSTALL_CONF}/00-example.conf")
1045+
1046+
list(APPEND _ALL_CONF_INSTALL_FILES "${LDC2_INSTALL_CONF}/00-example.conf")
1047+
endif()
1048+
endif()
1049+
1050+
installConf()
1051+
10481052
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
10491053
if(NOT DEFINED BASH_COMPLETION_COMPLETIONSDIR)
10501054
find_package(bash-completion QUIET)

cmake/Modules/LdcCommon.cmake

Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
# Common functionality shared by the compiler and the runtime build system.
2+
3+
function(append value)
4+
foreach(variable ${ARGN})
5+
if(${variable} STREQUAL "")
6+
set(${variable} "${value}" PARENT_SCOPE)
7+
else()
8+
set(${variable} "${${variable}} ${value}" PARENT_SCOPE)
9+
endif()
10+
endforeach(variable)
11+
endfunction()
12+
13+
function(defineIfUnset variable)
14+
if(ARGC EQUAL 1)
15+
set(args)
16+
else()
17+
list(SUBLIST ARGV 1 -1 args)
18+
endif()
19+
20+
if(NOT DEFINED ${variable})
21+
set(${variable} ${args} PARENT_SCOPE)
22+
endif()
23+
endfunction()
24+
25+
function(formatArray out_var)
26+
if(ARGC EQUAL 1)
27+
set(${out_var} "[]" PARENT_SCOPE)
28+
return()
29+
endif()
30+
31+
list(SUBLIST ARGV 1 -1 values)
32+
set(result "[")
33+
foreach(value ${values})
34+
set(result "${result}\n \"${value}\",")
35+
endforeach()
36+
set(result "${result}\n ]")
37+
38+
set(${out_var} "${result}" PARENT_SCOPE)
39+
endfunction()
40+
41+
function(formatArraySetting out_var name)
42+
if(ARGC EQUAL 2)
43+
# Ignore `value ~= []`
44+
set(${out_var} "" PARENT_SCOPE)
45+
return()
46+
endif()
47+
48+
list(SUBLIST ARGV 2 -1 values)
49+
list(GET values 0 maybe_override)
50+
if(maybe_override STREQUAL "OVERRIDE")
51+
set(operator "=")
52+
list(POP_FRONT values)
53+
else()
54+
set(operator "~=")
55+
endif()
56+
formatArray(array ${values})
57+
set(${out_var} "\n ${name} ${operator} ${array};" PARENT_SCOPE)
58+
endfunction()
59+
60+
function(formatScalarSetting out_var name value)
61+
if(value STREQUAL "")
62+
set(${out_var} "" PARENT_SCOPE)
63+
else()
64+
set(${out_var} "\n ${name} = \"${value}\";" PARENT_SCOPE)
65+
endif()
66+
endfunction()
67+
68+
69+
# Create a ldc2.conf section
70+
#
71+
# Example:
72+
#
73+
# makeConfSectionImpl(
74+
# FILEPATH "${CMAKE_BINARY_DIR}/ldc2.conf"
75+
# # The output filename
76+
#
77+
# SECTION "^wasm(32|64)-"
78+
# # A regex to match a target triple or "default"
79+
#
80+
# SWITCHES -d-version=foo -L--linker-flag
81+
# # The `switches` list
82+
#
83+
# POST_SWITCHES OVERRIDE -I/path/to/druntime/src
84+
# # The `post-switches` list
85+
#
86+
# LIB_DIRS "${CMAKE_BINARY_DIR}/lib64"
87+
# # The `lib-dirs` list
88+
#
89+
# RPATH "/path/to/dir"
90+
# # The `rpath` value
91+
# )
92+
#
93+
# Would generate:
94+
#
95+
# $ cat "${CMAKE_BINARY_DIR}/ldc2.conf"
96+
# "^wasm(32|64)-":
97+
# {
98+
# switches ~= [
99+
# "-d-version=foo",
100+
# "-L--linker-flag",
101+
# ];
102+
# post-switches = [
103+
# "-I/path/to/druntime/src"
104+
# ];
105+
# lib-dirs ~= [
106+
# "${CMAKE_BINARY_DIR}/lib64"
107+
# ];
108+
# rpath = "/path/to/dir"
109+
# }
110+
#
111+
# You don't need to pass all setting keys, only the ones that have values.
112+
#
113+
# Array settings (SWITCHES, POST_SWITCHES, LIB_DIRS) may start with an
114+
# OVERRIDE signifying that they should overwrite the (possibly)
115+
# previously stored values. The default is to append to them.
116+
function(makeConfSectionImpl)
117+
set(oneValueArgs FILEPATH SECTION RPATH)
118+
set(multiValueArgs SWITCHES POST_SWITCHES LIB_DIRS)
119+
cmake_parse_arguments(args "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
120+
121+
foreach(var FILEPATH SECTION)
122+
if(NOT DEFINED args_${var})
123+
message(SEND_ERROR "Expected ${var} argument")
124+
endif()
125+
endforeach()
126+
if(DEFINED args_UNPARSED_ARGUMENTS)
127+
message(SEND_ERROR "Unexpected arguments: ${args_UNPARSED_ARGUMENTS}")
128+
endif()
129+
130+
formatArraySetting(switches "switches" ${args_SWITCHES})
131+
formatArraySetting(post_switches "post-switches" ${args_POST_SWITCHES})
132+
formatArraySetting(lib_dirs "lib-dirs" ${args_LIB_DIRS})
133+
formatScalarSetting(rpath "rpath" "${args_RPATH}")
134+
135+
file(WRITE "${args_FILEPATH}"
136+
"\"${args_SECTION}\":\n"
137+
"{"
138+
"${switches}"
139+
"${post_switches}"
140+
"${lib_dirs}"
141+
"${rpath}"
142+
"\n};\n\n"
143+
)
144+
endfunction()
145+
146+
# Create a ldc2.conf section
147+
#
148+
# Example:
149+
#
150+
# makeConfSection(
151+
# NAME 40-runtime
152+
# # a unique name for the file that will store this section
153+
#
154+
# SECTION "x86_64-.*-linux-gnu"
155+
# # A regex for a target triple or the string "default"
156+
#
157+
# BUILD
158+
# # Settings for ldc2.conf when part of this cmake project
159+
# SWITCHES -a -b
160+
# POST_SWITCHES -I${CMAKE_SOURCE_DIR}/runtime/import -c
161+
#
162+
# INSTALL
163+
# # Settings for ldc2.conf when installed on a user system
164+
# SWITCHES OVERRIDE -bar
165+
# LIB_DIRS "${CMAKE_INSTALL_PREFIX}/lib"
166+
# RPATH "${CMAKE_INSTALL_PREFIX}/lib"
167+
# )
168+
#
169+
# The possible settings are described by makeConfSectionImpl.
170+
#
171+
# As a shortcut the BUILD and INSTALL arguments may be omitted, making the
172+
# settings be applied to both build and install ldc2.conf. Example:
173+
#
174+
# makeConfSection(NAME 10-example SECTION default
175+
# SWITCHES -my-important-switch
176+
# )
177+
#
178+
# Is equivalent to:
179+
#
180+
# makeConfSection(NAME 10-example SECTION default
181+
# BUILD
182+
# SWITCHES -my-important-switch
183+
# INSTALL
184+
# SWITCHES -my-important-switch
185+
# )
186+
#
187+
# It is also possible to generate a configuration file for either only the build
188+
# or only the install. Simply pass only BUILD or INSTALL to this function.
189+
function(makeConfSection)
190+
set(oneValueArgs NAME SECTION)
191+
set(multiValueArgs BUILD INSTALL)
192+
cmake_parse_arguments(PARSE_ARGV 0 args "" "${oneValueArgs}" "${multiValueArgs}")
193+
194+
foreach(var ${oneValueArgs})
195+
if(NOT DEFINED args_${var})
196+
message(SEND_ERROR "Expected defined ${var} argument")
197+
endif()
198+
endforeach()
199+
if(DEFINED args_UNPARSED_ARGUMENTS)
200+
if(NOT DEFINED args_BUILD AND NOT DEFINED args_INSTALL)
201+
set(args_BUILD "${args_UNPARSED_ARGUMENTS}")
202+
set(args_INSTALL "${args_UNPARSED_ARGUMENTS}")
203+
else()
204+
message(SEND_ERROR "Unexpected arguments: ${args_UNPARSED_ARGUMENTS}")
205+
endif()
206+
endif()
207+
208+
if(args_BUILD)
209+
set(build_conf "${LDC2_BUILD_CONF}/${args_NAME}.conf")
210+
makeConfSectionImpl(FILEPATH "${build_conf}" SECTION "${args_SECTION}" ${args_BUILD})
211+
endif()
212+
if(args_INSTALL)
213+
set(install_conf "${LDC2_INSTALL_CONF}/${args_NAME}.conf")
214+
makeConfSectionImpl(FILEPATH "${install_conf}" SECTION "${args_SECTION}" ${args_INSTALL})
215+
if(CONF_PREFER_DIR)
216+
if(NOT DONT_INSTALL_CONF)
217+
install(FILES "${install_conf}" DESTINATION "${CONF_INST_DIR}/ldc2.conf")
218+
endif()
219+
else()
220+
get_filename_component(install_path "${install_path}" ABSOLUTE)
221+
list(APPEND _ALL_CONF_INSTALL_FILES "${install_conf}")
222+
set(_ALL_CONF_INSTALL_FILES "${_ALL_CONF_INSTALL_FILES}" PARENT_SCOPE)
223+
endif()
224+
endif()
225+
endfunction()
226+
227+
function(installConf)
228+
if(CONF_PREFER_DIR OR DONT_INSTALL_CONF)
229+
return()
230+
endif()
231+
232+
list(SORT _ALL_CONF_INSTALL_FILES COMPARE FILE_BASENAME)
233+
234+
set(out_dir "${CMAKE_BINARY_DIR}/etc")
235+
make_directory("${out_dir}")
236+
set(output_conf_file "${out_dir}/ldc2_install.conf")
237+
238+
# The first argument is the output file name, for the below scripts
239+
set(cmd_args "${output_conf_file}" "${_ALL_CONF_INSTALL_FILES}")
240+
if(WIN32)
241+
file(WRITE "${CMAKE_BINARY_DIR}/cat_into.bat"
242+
# Yes, we are relying on `> %1` to clear the file content
243+
# before the file is "cat"ed because SHIFT does not shift
244+
# %*.
245+
"@ECHO OFF\ntype %* > %1\n")
246+
set(cmd "${CMAKE_BINARY_DIR}/cat_into.bat")
247+
# Convert the cmake /-style paths to \
248+
list(TRANSFORM cmd_args REPLACE "/" "\\\\")
249+
else()
250+
set(cmd sh -c "cat \"\$@\" > \"\$1\"" script)
251+
endif()
252+
add_custom_command(OUTPUT "${output_conf_file}"
253+
# cat all files into OUTPUT
254+
COMMAND ${cmd} ${cmd_args}
255+
VERBATIM
256+
DEPENDS ${_ALL_CONF_INSTALL_FILES}
257+
)
258+
add_custom_target(gen_conf_file ALL DEPENDS "${output_conf_file}")
259+
install(FILES "${output_conf_file}" DESTINATION "${CONF_INST_DIR}" RENAME ldc2.conf)
260+
endfunction()
261+
262+
# Generally, we want to install everything into CMAKE_INSTALL_PREFIX, but when
263+
# it is /usr, put the config files into /etc to meet common practice.
264+
if(NOT DEFINED SYSCONF_INSTALL_DIR)
265+
if(CMAKE_INSTALL_PREFIX STREQUAL "/usr")
266+
set(SYSCONF_INSTALL_DIR "/etc")
267+
else()
268+
set(SYSCONF_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/etc")
269+
endif()
270+
endif()
271+
272+
set(CONF_INST_DIR ${SYSCONF_INSTALL_DIR} CACHE PATH "Directory in which to install ldc2.conf")
273+
if(CONF_INST_DIR STREQUAL "")
274+
set(DONT_INSTALL_CONF TRUE)
275+
else()
276+
set(DONT_INSTALL_CONF FALSE)
277+
endif()
278+
279+
option(CONF_PREFER_DIR "Prefer installing ldc2.conf as a directory")
280+
# Avoid making the D code check for all cmake truthy values
281+
if(CONF_PREFER_DIR)
282+
set(CONF_PREFER_DIR_D_BOOLEAN true)
283+
else()
284+
set(CONF_PREFER_DIR_D_BOOLEAN false)
285+
endif()
286+
287+
set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/include/d CACHE PATH "Path to install D modules to")
288+
289+
defineIfUnset(LDC2_BUILD_CONF "${CMAKE_BINARY_DIR}/etc/ldc2.conf")
290+
defineIfUnset(LDC2_INSTALL_CONF "${CMAKE_BINARY_DIR}/etc/ldc2_install.conf.d")

0 commit comments

Comments
 (0)