1include(ExternalProject)
2
3# llvm_ExternalProject_BuildCmd(out_var target)
4#   Utility function for constructing command lines for external project targets
5function(llvm_ExternalProject_BuildCmd out_var target bin_dir)
6  cmake_parse_arguments(ARG "" "CONFIGURATION" "" ${ARGN})
7  if(NOT ARG_CONFIGURATION)
8    set(ARG_CONFIGURATION "$<CONFIG>")
9  endif()
10  if (CMAKE_GENERATOR MATCHES "Make")
11    # Use special command for Makefiles to support parallelism.
12    set(${out_var} "$(MAKE)" "-C" "${bin_dir}" "${target}" PARENT_SCOPE)
13  else()
14    set(tool_args "${LLVM_EXTERNAL_PROJECT_BUILD_TOOL_ARGS}")
15    if(NOT tool_args STREQUAL "")
16      string(CONFIGURE "${tool_args}" tool_args @ONLY)
17      string(PREPEND tool_args "-- ")
18      separate_arguments(tool_args UNIX_COMMAND "${tool_args}")
19    endif()
20    set(${out_var} ${CMAKE_COMMAND} --build ${bin_dir} --target ${target}
21                                    --config ${ARG_CONFIGURATION} ${tool_args} PARENT_SCOPE)
22  endif()
23endfunction()
24
25# is_msvc_triple(out_var triple)
26#   Checks whether the passed triple refers to an MSVC environment
27function(is_msvc_triple out_var triple)
28  if (triple MATCHES ".*-windows-msvc.*")
29    set(${out_var} TRUE PARENT_SCOPE)
30  else()
31    set(${out_var} FALSE PARENT_SCOPE)
32  endif()
33endfunction()
34
35
36# llvm_ExternalProject_Add(name source_dir ...
37#   USE_TOOLCHAIN
38#     Use just-built tools (see TOOLCHAIN_TOOLS)
39#   EXCLUDE_FROM_ALL
40#     Exclude this project from the all target
41#   NO_INSTALL
42#     Don't generate install targets for this project
43#   ALWAYS_CLEAN
44#     Always clean the sub-project before building
45#   CMAKE_ARGS arguments...
46#     Optional cmake arguments to pass when configuring the project
47#   TOOLCHAIN_TOOLS targets...
48#     Targets for toolchain tools (defaults to clang;lld)
49#   DEPENDS targets...
50#     Targets that this project depends on
51#   EXTRA_TARGETS targets...
52#     Extra targets in the subproject to generate targets for
53#   PASSTHROUGH_PREFIXES prefix...
54#     Extra variable prefixes (name is always included) to pass down
55#   STRIP_TOOL path
56#     Use provided strip tool instead of the default one.
57#   TARGET_TRIPLE triple
58#     Optional target triple to pass to the compiler
59#   )
60function(llvm_ExternalProject_Add name source_dir)
61  cmake_parse_arguments(ARG
62    "USE_TOOLCHAIN;EXCLUDE_FROM_ALL;NO_INSTALL;ALWAYS_CLEAN"
63    "SOURCE_DIR"
64    "CMAKE_ARGS;TOOLCHAIN_TOOLS;RUNTIME_LIBRARIES;DEPENDS;EXTRA_TARGETS;PASSTHROUGH_PREFIXES;STRIP_TOOL;TARGET_TRIPLE"
65    ${ARGN})
66  canonicalize_tool_name(${name} nameCanon)
67
68  foreach(arg ${ARG_CMAKE_ARGS})
69    if(arg MATCHES "^-DCMAKE_SYSTEM_NAME=")
70      string(REGEX REPLACE "^-DCMAKE_SYSTEM_NAME=(.*)$" "\\1" _cmake_system_name "${arg}")
71    endif()
72  endforeach()
73
74  # If CMAKE_SYSTEM_NAME is not set explicitly in the arguments passed to us,
75  # reflect CMake's own default.
76  if (NOT _cmake_system_name)
77    set(_cmake_system_name "${CMAKE_HOST_SYSTEM_NAME}")
78  endif()
79
80  if(NOT ARG_TARGET_TRIPLE)
81    set(target_triple ${LLVM_DEFAULT_TARGET_TRIPLE})
82  else()
83    set(target_triple ${ARG_TARGET_TRIPLE})
84  endif()
85
86  is_msvc_triple(is_msvc_target "${target_triple}")
87
88  if(NOT ARG_TOOLCHAIN_TOOLS)
89    set(ARG_TOOLCHAIN_TOOLS clang)
90    # AIX 64-bit XCOFF and big AR format is not yet supported in some of these tools.
91    if(NOT _cmake_system_name STREQUAL AIX)
92      list(APPEND ARG_TOOLCHAIN_TOOLS lld llvm-ar llvm-ranlib llvm-nm llvm-objdump)
93      if(_cmake_system_name STREQUAL Darwin)
94        list(APPEND ARG_TOOLCHAIN_TOOLS llvm-libtool-darwin llvm-lipo)
95      elseif(is_msvc_target)
96        list(APPEND ARG_TOOLCHAIN_TOOLS llvm-lib llvm-rc)
97        if (LLVM_ENABLE_LIBXML2)
98          list(APPEND ARG_TOOLCHAIN_TOOLS llvm-mt)
99        endif()
100      else()
101        # TODO: These tools don't fully support Mach-O format yet.
102        list(APPEND ARG_TOOLCHAIN_TOOLS llvm-objcopy llvm-strip llvm-readelf)
103      endif()
104    endif()
105  endif()
106  foreach(tool ${ARG_TOOLCHAIN_TOOLS})
107    if(TARGET ${tool})
108      list(APPEND TOOLCHAIN_TOOLS ${tool})
109
110      # $<TARGET_FILE:tgt> only works on add_executable or add_library targets
111      # The below logic mirrors cmake's own implementation
112      get_target_property(target_type "${tool}" TYPE)
113      if(NOT target_type STREQUAL "OBJECT_LIBRARY" AND
114         NOT target_type STREQUAL "UTILITY" AND
115         NOT target_type STREQUAL "GLOBAL_TARGET" AND
116         NOT target_type STREQUAL "INTERFACE_LIBRARY")
117        list(APPEND TOOLCHAIN_BINS $<TARGET_FILE:${tool}>)
118      endif()
119
120    endif()
121  endforeach()
122
123  if(NOT ARG_RUNTIME_LIBRARIES)
124    set(ARG_RUNTIME_LIBRARIES compiler-rt libcxx)
125  endif()
126  foreach(lib ${ARG_RUNTIME_LIBRARIES})
127    if(TARGET ${lib})
128      list(APPEND RUNTIME_LIBRARIES ${lib})
129    endif()
130  endforeach()
131
132  if(ARG_ALWAYS_CLEAN)
133    set(always_clean clean)
134  endif()
135
136  if(clang IN_LIST TOOLCHAIN_TOOLS)
137    set(CLANG_IN_TOOLCHAIN On)
138  endif()
139
140  if(RUNTIME_LIBRARIES AND CLANG_IN_TOOLCHAIN)
141    list(APPEND TOOLCHAIN_BINS ${RUNTIME_LIBRARIES})
142  endif()
143
144  set(STAMP_DIR ${CMAKE_CURRENT_BINARY_DIR}/${name}-stamps/)
145  set(BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${name}-bins/)
146
147  add_custom_target(${name}-clear
148    COMMAND ${CMAKE_COMMAND} -E remove_directory ${BINARY_DIR}
149    COMMAND ${CMAKE_COMMAND} -E remove_directory ${STAMP_DIR}
150    COMMENT "Clobbering ${name} build and stamp directories"
151    USES_TERMINAL
152    )
153
154  # Find all variables that start with a prefix and propagate them through
155  get_cmake_property(variableNames VARIABLES)
156
157  list(APPEND ARG_PASSTHROUGH_PREFIXES ${nameCanon})
158  foreach(prefix ${ARG_PASSTHROUGH_PREFIXES})
159    foreach(variableName ${variableNames})
160      if(variableName MATCHES "^${prefix}")
161        string(REPLACE ";" "|" value "${${variableName}}")
162        list(APPEND PASSTHROUGH_VARIABLES
163          -D${variableName}=${value})
164      endif()
165    endforeach()
166  endforeach()
167
168  # Populate the non-project-specific passthrough variables
169  foreach(variableName ${LLVM_EXTERNAL_PROJECT_PASSTHROUGH})
170    if(DEFINED ${variableName})
171      if("${${variableName}}" STREQUAL "")
172        set(value "")
173      else()
174        string(REPLACE ";" "|" value "${${variableName}}")
175      endif()
176      list(APPEND PASSTHROUGH_VARIABLES
177        -D${variableName}=${value})
178    endif()
179  endforeach()
180
181  if(ARG_USE_TOOLCHAIN AND NOT CMAKE_CROSSCOMPILING)
182    if(CLANG_IN_TOOLCHAIN)
183      if(is_msvc_target)
184        set(compiler_args -DCMAKE_C_COMPILER=${LLVM_RUNTIME_OUTPUT_INTDIR}/clang-cl${CMAKE_EXECUTABLE_SUFFIX}
185                          -DCMAKE_CXX_COMPILER=${LLVM_RUNTIME_OUTPUT_INTDIR}/clang-cl${CMAKE_EXECUTABLE_SUFFIX}
186                          -DCMAKE_ASM_COMPILER=${LLVM_RUNTIME_OUTPUT_INTDIR}/clang-cl${CMAKE_EXECUTABLE_SUFFIX})
187      else()
188        set(compiler_args -DCMAKE_C_COMPILER=${LLVM_RUNTIME_OUTPUT_INTDIR}/clang${CMAKE_EXECUTABLE_SUFFIX}
189                          -DCMAKE_CXX_COMPILER=${LLVM_RUNTIME_OUTPUT_INTDIR}/clang++${CMAKE_EXECUTABLE_SUFFIX}
190                          -DCMAKE_ASM_COMPILER=${LLVM_RUNTIME_OUTPUT_INTDIR}/clang${CMAKE_EXECUTABLE_SUFFIX})
191      endif()
192    endif()
193    if(lld IN_LIST TOOLCHAIN_TOOLS)
194      if(is_msvc_target)
195        list(APPEND compiler_args -DCMAKE_LINKER=${LLVM_RUNTIME_OUTPUT_INTDIR}/lld-link${CMAKE_EXECUTABLE_SUFFIX})
196      elseif(NOT _cmake_system_name STREQUAL Darwin)
197        list(APPEND compiler_args -DCMAKE_LINKER=${LLVM_RUNTIME_OUTPUT_INTDIR}/ld.lld${CMAKE_EXECUTABLE_SUFFIX})
198      endif()
199    endif()
200    if(llvm-ar IN_LIST TOOLCHAIN_TOOLS)
201      if(is_msvc_target)
202        list(APPEND compiler_args -DCMAKE_AR=${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-lib${CMAKE_EXECUTABLE_SUFFIX})
203      else()
204        list(APPEND compiler_args -DCMAKE_AR=${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-ar${CMAKE_EXECUTABLE_SUFFIX})
205      endif()
206    endif()
207    if(llvm-libtool-darwin IN_LIST TOOLCHAIN_TOOLS)
208      list(APPEND compiler_args -DCMAKE_LIBTOOL=${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-libtool-darwin${CMAKE_EXECUTABLE_SUFFIX})
209    endif()
210    if(llvm-lipo IN_LIST TOOLCHAIN_TOOLS)
211      list(APPEND compiler_args -DCMAKE_LIPO=${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-lipo${CMAKE_EXECUTABLE_SUFFIX})
212    endif()
213    if(llvm-ranlib IN_LIST TOOLCHAIN_TOOLS)
214      list(APPEND compiler_args -DCMAKE_RANLIB=${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-ranlib${CMAKE_EXECUTABLE_SUFFIX})
215    endif()
216    if(llvm-nm IN_LIST TOOLCHAIN_TOOLS)
217      list(APPEND compiler_args -DCMAKE_NM=${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-nm${CMAKE_EXECUTABLE_SUFFIX})
218    endif()
219    if(llvm-objdump IN_LIST TOOLCHAIN_TOOLS)
220      list(APPEND compiler_args -DCMAKE_OBJDUMP=${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-objdump${CMAKE_EXECUTABLE_SUFFIX})
221    endif()
222    if(llvm-objcopy IN_LIST TOOLCHAIN_TOOLS)
223      list(APPEND compiler_args -DCMAKE_OBJCOPY=${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-objcopy${CMAKE_EXECUTABLE_SUFFIX})
224    endif()
225    if(llvm-strip IN_LIST TOOLCHAIN_TOOLS AND NOT ARG_STRIP_TOOL)
226      list(APPEND compiler_args -DCMAKE_STRIP=${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-strip${CMAKE_EXECUTABLE_SUFFIX})
227    endif()
228    if(llvm-readelf IN_LIST TOOLCHAIN_TOOLS)
229      list(APPEND compiler_args -DCMAKE_READELF=${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-readelf${CMAKE_EXECUTABLE_SUFFIX})
230    endif()
231    if(llvm-mt IN_LIST TOOLCHAIN_TOOLS AND is_msvc_target)
232      list(APPEND compiler_args -DCMAKE_MT=${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-mt${CMAKE_EXECUTABLE_SUFFIX})
233    endif()
234    if(llvm-rc IN_LIST TOOLCHAIN_TOOLS AND is_msvc_target)
235      list(APPEND compiler_args -DCMAKE_RC_COMPILER=${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-rc${CMAKE_EXECUTABLE_SUFFIX})
236    endif()
237    list(APPEND ARG_DEPENDS ${TOOLCHAIN_TOOLS})
238  endif()
239
240  if(ARG_STRIP_TOOL)
241    list(APPEND compiler_args -DCMAKE_STRIP=${ARG_STRIP_TOOL})
242  endif()
243
244  add_custom_command(
245    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${name}-clobber-stamp
246    DEPENDS ${ARG_DEPENDS}
247    COMMAND ${CMAKE_COMMAND} -E touch ${BINARY_DIR}/CMakeCache.txt
248    COMMAND ${CMAKE_COMMAND} -E touch ${STAMP_DIR}/${name}-mkdir
249    COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/${name}-clobber-stamp
250    COMMENT "Clobbering bootstrap build and stamp directories"
251    )
252
253  add_custom_target(${name}-clobber
254    DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${name}-clobber-stamp)
255
256  if(ARG_EXCLUDE_FROM_ALL)
257    set(exclude EXCLUDE_FROM_ALL 1)
258  endif()
259
260  if(CMAKE_SYSROOT)
261    set(sysroot_arg -DCMAKE_SYSROOT=${CMAKE_SYSROOT})
262  endif()
263
264  if(CMAKE_CROSSCOMPILING OR _cmake_system_name STREQUAL AIX)
265    set(compiler_args -DCMAKE_ASM_COMPILER=${CMAKE_ASM_COMPILER}
266                      -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
267                      -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
268                      -DCMAKE_LINKER=${CMAKE_LINKER}
269                      -DCMAKE_AR=${CMAKE_AR}
270                      -DCMAKE_RANLIB=${CMAKE_RANLIB}
271                      -DCMAKE_LIPO=${CMAKE_LIPO}
272                      -DCMAKE_NM=${CMAKE_NM}
273                      -DCMAKE_OBJCOPY=${CMAKE_OBJCOPY}
274                      -DCMAKE_OBJDUMP=${CMAKE_OBJDUMP}
275                      -DCMAKE_STRIP=${CMAKE_STRIP}
276                      -DCMAKE_READELF=${CMAKE_READELF})
277  endif()
278  if(CMAKE_CROSSCOMPILING)
279    set(llvm_config_path ${LLVM_CONFIG_PATH})
280
281    if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
282      string(REGEX MATCH "^[0-9]+" CLANG_VERSION_MAJOR
283             ${PACKAGE_VERSION})
284      if(DEFINED CLANG_RESOURCE_DIR AND NOT CLANG_RESOURCE_DIR STREQUAL "")
285        set(resource_dir ${LLVM_TOOLS_BINARY_DIR}/${CLANG_RESOURCE_DIR})
286      else()
287        set(resource_dir "${LLVM_LIBRARY_DIR}/clang/${CLANG_VERSION_MAJOR}")
288      endif()
289      set(flag_types ASM C CXX MODULE_LINKER SHARED_LINKER EXE_LINKER)
290      foreach(type ${flag_types})
291        set(${type}_flag -DCMAKE_${type}_FLAGS=-resource-dir=${resource_dir})
292      endforeach()
293      string(REPLACE ";" "|" flag_string "${flag_types}")
294      foreach(arg ${ARG_CMAKE_ARGS})
295        if(arg MATCHES "^-DCMAKE_(${flag_string})_FLAGS")
296          foreach(type ${flag_types})
297            if(arg MATCHES "^-DCMAKE_${type}_FLAGS")
298              string(REGEX REPLACE "^-DCMAKE_${type}_FLAGS=(.*)$" "\\1" flag_value "${arg}")
299              set(${type}_flag "${${type}_flag} ${flag_value}")
300            endif()
301          endforeach()
302        else()
303          list(APPEND cmake_args ${arg})
304        endif()
305      endforeach()
306      foreach(type ${flag_types})
307        list(APPEND cmake_args ${${type}_flag})
308      endforeach()
309    else()
310      set(cmake_args ${ARG_CMAKE_ARGS})
311    endif()
312  else()
313    set(llvm_config_path "$<TARGET_FILE:llvm-config>")
314    set(cmake_args ${ARG_CMAKE_ARGS})
315  endif()
316
317  if(ARG_TARGET_TRIPLE)
318    list(APPEND compiler_args -DCMAKE_C_COMPILER_TARGET=${ARG_TARGET_TRIPLE})
319    list(APPEND compiler_args -DCMAKE_CXX_COMPILER_TARGET=${ARG_TARGET_TRIPLE})
320    list(APPEND compiler_args -DCMAKE_ASM_COMPILER_TARGET=${ARG_TARGET_TRIPLE})
321  endif()
322
323  if(CMAKE_VERBOSE_MAKEFILE)
324    set(verbose -DCMAKE_VERBOSE_MAKEFILE=ON)
325  endif()
326
327  ExternalProject_Add(${name}
328    DEPENDS ${ARG_DEPENDS} llvm-config
329    ${name}-clobber
330    PREFIX ${CMAKE_BINARY_DIR}/projects/${name}
331    SOURCE_DIR ${source_dir}
332    STAMP_DIR ${STAMP_DIR}
333    BINARY_DIR ${BINARY_DIR}
334    ${exclude}
335    CMAKE_ARGS ${${nameCanon}_CMAKE_ARGS}
336               --no-warn-unused-cli
337               ${compiler_args}
338               ${verbose}
339               -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}
340               ${sysroot_arg}
341               -DLLVM_BINARY_DIR=${PROJECT_BINARY_DIR}
342               -DLLVM_CONFIG_PATH=${llvm_config_path}
343               -DLLVM_ENABLE_WERROR=${LLVM_ENABLE_WERROR}
344               -DLLVM_HOST_TRIPLE=${LLVM_HOST_TRIPLE}
345               -DLLVM_HAVE_LINK_VERSION_SCRIPT=${LLVM_HAVE_LINK_VERSION_SCRIPT}
346               -DLLVM_USE_RELATIVE_PATHS_IN_DEBUG_INFO=${LLVM_USE_RELATIVE_PATHS_IN_DEBUG_INFO}
347               -DLLVM_USE_RELATIVE_PATHS_IN_FILES=${LLVM_USE_RELATIVE_PATHS_IN_FILES}
348               -DLLVM_LIT_ARGS=${LLVM_LIT_ARGS}
349               -DLLVM_SOURCE_PREFIX=${LLVM_SOURCE_PREFIX}
350               -DPACKAGE_VERSION=${PACKAGE_VERSION}
351               -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
352               -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}
353               -DCMAKE_EXPORT_COMPILE_COMMANDS=1
354               ${cmake_args}
355               ${PASSTHROUGH_VARIABLES}
356    INSTALL_COMMAND ""
357    STEP_TARGETS configure build
358    BUILD_ALWAYS 1
359    USES_TERMINAL_CONFIGURE 1
360    USES_TERMINAL_BUILD 1
361    USES_TERMINAL_INSTALL 1
362    LIST_SEPARATOR |
363    )
364
365  if(ARG_USE_TOOLCHAIN)
366    set(force_deps DEPENDS ${TOOLCHAIN_BINS})
367  endif()
368
369  llvm_ExternalProject_BuildCmd(run_clean clean ${BINARY_DIR})
370  ExternalProject_Add_Step(${name} clean
371    COMMAND ${run_clean}
372    COMMENT "Cleaning ${name}..."
373    DEPENDEES configure
374    ${force_deps}
375    WORKING_DIRECTORY ${BINARY_DIR}
376    EXCLUDE_FROM_MAIN 1
377    USES_TERMINAL 1
378    )
379  ExternalProject_Add_StepTargets(${name} clean)
380
381  if(ARG_USE_TOOLCHAIN)
382    add_dependencies(${name}-clean ${name}-clobber)
383    set_target_properties(${name}-clean PROPERTIES
384      SOURCES ${CMAKE_CURRENT_BINARY_DIR}/${name}-clobber-stamp)
385  endif()
386
387  if(NOT ARG_NO_INSTALL)
388    install(CODE "execute_process\(COMMAND \${CMAKE_COMMAND} -DCMAKE_INSTALL_PREFIX=\${CMAKE_INSTALL_PREFIX} -DCMAKE_INSTALL_DO_STRIP=\${CMAKE_INSTALL_DO_STRIP} -P ${BINARY_DIR}/cmake_install.cmake\)"
389      COMPONENT ${name})
390
391    add_llvm_install_targets(install-${name}
392                             DEPENDS ${name}
393                             COMPONENT ${name})
394  endif()
395
396  # Add top-level targets
397  foreach(target ${ARG_EXTRA_TARGETS})
398    if(DEFINED ${target})
399      set(external_target "${${target}}")
400    else()
401      set(external_target "${target}")
402    endif()
403    llvm_ExternalProject_BuildCmd(build_runtime_cmd ${external_target} ${BINARY_DIR})
404    add_custom_target(${target}
405      COMMAND ${build_runtime_cmd}
406      DEPENDS ${name}-configure
407      WORKING_DIRECTORY ${BINARY_DIR}
408      VERBATIM
409      USES_TERMINAL)
410  endforeach()
411endfunction()
412