2 # <https://github.com/nemequ/gnome-cmake>
4 # This file contains functions which can be used to integrate Vala
5 # compilation with CMake. It is intended as a replacement for Jakob
6 # Westhoff's FindVala.cmake and UseVala.cmake. It uses fast-vapis for
7 # faster parallel compilation, and per-target directories for
8 # generated sources to allow reusing source files across, even with
13 # Copyright (c) 2016 Evan Nemerson <evan@nemerson.com>
15 # Permission is hereby granted, free of charge, to any person
16 # obtaining a copy of this software and associated documentation
17 # files (the "Software"), to deal in the Software without
18 # restriction, including without limitation the rights to use, copy,
19 # modify, merge, publish, distribute, sublicense, and/or sell copies
20 # of the Software, and to permit persons to whom the Software is
21 # furnished to do so, subject to the following conditions:
23 # The above copyright notice and this permission notice shall be
24 # included in all copies or substantial portions of the Software.
26 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
30 # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
31 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
32 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
33 # DEALINGS IN THE SOFTWARE.
35 set(VALAC_NAMES valac)
37 set(_FIND_VALA_CURRENT_VERSION 98)
38 while(_FIND_VALA_CURRENT_VERSION GREATER 0)
39 list(APPEND VALAC_NAME "valac-1.${_FIND_VALA_CURRENT_VERSION}")
40 math(EXPR _FIND_VALA_CURRENT_VERSION "${_FIND_VALA_CURRENT_VERSION} - 2")
42 set(_FIND_VALA_CURRENT_VERSION 98)
43 while(_FIND_VALA_CURRENT_VERSION GREATER 0)
44 list(APPEND VALAC_NAME "valac-1.${_FIND_VALA_CURRENT_VERSION}")
45 math(EXPR _FIND_VALA_CURRENT_VERSION "${_FIND_VALA_CURRENT_VERSION} - 2")
47 unset(_FIND_VALA_CURRENT_VERSION)
49 find_program(VALA_EXECUTABLE
51 mark_as_advanced(VALA_EXECUTABLE)
56 # Determine valac version
57 execute_process(COMMAND ${VALA_EXECUTABLE} "--version"
58 OUTPUT_VARIABLE VALA_VERSION)
59 string(REGEX REPLACE "^.*Vala ([0-9]+\\.[0-9]+\\.[0-9]+(\\.[0-9]+(\\-[0-9a-f]+)?)?).*$" "\\1" VALA_VERSION "${VALA_VERSION}")
61 add_executable(valac IMPORTED)
62 set_property(TARGET valac PROPERTY IMPORTED_LOCATION "${VALA_EXECUTABLE}")
63 endif(VALA_EXECUTABLE)
65 include(FindPackageHandleStandardArgs)
66 find_package_handle_standard_args(Vala
67 REQUIRED_VARS VALA_EXECUTABLE
68 VERSION_VAR VALA_VERSION)
70 function(_vala_mkdir_for_file file)
71 get_filename_component(dir "${file}" DIRECTORY)
72 file(MAKE_DIRECTORY "${dir}")
75 macro(_vala_parse_source_file_path source)
77 set (oneValueArgs SOURCE TYPE OUTPUT_PATH OUTPUT_DIR GENERATED_SOURCE FAST_VAPI)
79 cmake_parse_arguments(VALAPATH "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
82 unset (multiValueArgs)
85 get_filename_component("${VALAPATH_SOURCE}" "${source}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
89 string(REGEX MATCH "[^\\.]+$" "${VALAPATH_TYPE}" "${source}")
90 string(TOLOWER "${${VALAPATH_TYPE}}" "${VALAPATH_TYPE}")
93 if(VALAPATH_OUTPUT_PATH OR VALAPATH_GENERATED_SOURCE OR VALAPATH_OUTPUT_DIR OR VALAPATH_FAST_VAPI)
94 get_filename_component(srcfile "${source}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
96 string(LENGTH "${CMAKE_BINARY_DIR}" dirlen)
97 string(SUBSTRING "${srcfile}" 0 ${dirlen} tmp)
98 if("${CMAKE_BINARY_DIR}" STREQUAL "${tmp}")
99 string(SUBSTRING "${srcfile}" ${dirlen} -1 tmp)
100 set(outpath "build${tmp}")
102 string(LENGTH "${CMAKE_SOURCE_DIR}" dirlen)
103 string(SUBSTRING "${srcfile}" 0 ${dirlen} tmp)
104 if("${CMAKE_SOURCE_DIR}" STREQUAL "${tmp}")
105 string(SUBSTRING "${srcfile}" ${dirlen} -1 tmp)
106 set(outpath "source${tmp}")
108 # TODO: this probably doesn't work correctly on Windows…
109 set(outpath "root${tmp}")
118 if(VALAPATH_OUTPUT_PATH)
119 set("${VALAPATH_OUTPUT_PATH}" "${outpath}")
122 if(VALAPATH_GENERATED_SOURCE)
123 string(REGEX REPLACE "\\.(vala|gs)$" ".c" "${VALAPATH_GENERATED_SOURCE}" "${outpath}")
126 if(VALAPATH_FAST_VAPI)
127 string(REGEX REPLACE "\\.(vala|gs)$" ".vapi" "${VALAPATH_FAST_VAPI}" "${outpath}")
130 if(VALAPATH_OUTPUT_DIR)
131 get_filename_component("${VALAPATH_OUTPUT_DIR}" "${outpath}" DIRECTORY)
137 # vala_precompile_target(
141 # [VAPI vapi-name.vapi]
142 # [GIR name-version.gir]
148 # This function will use valac to generate C code.
150 # This function uses fast VAPIs to help improve parallelization and
151 # incremental build times. The problem with this is that CMake
152 # doesn't allow file-level dependencies across directories; if you're
153 # generating code in one directory (for example, a library) and would
154 # like to use it in another directory and are building in parallel,
155 # the build can fail. To prevent this, this function will create a
156 # ${TARGET}-vala top-level target (which *is* usable from other
162 # Target to create; it's generally best to make this similar to
163 # your executable or library target name (e.g., for a "foo"
164 # executable, "foo-vala" might be a good name), but not
165 # technically required.
167 # Variable in which to store the list of generated sources (which
168 # you should pass to add_executable or add_library).
170 # Vala sources to generate C from. You should include *.vala,
171 # *.gs, and uninstalled *.vapi files here; you may also include
172 # C/C++ sources (they will simply be passed directly through to
173 # the GENERATED_SOURCES variable).
175 # If you would like to have valac generate a VAPI (basically, if
176 # you are generating a library not an executable), pass the file
178 # GIR name-version.gir
179 # If you would like to have valac generate a GIR, pass the file
182 # If you would like to have valac generate a C header, pass the
185 # List of flags you wish to pass to valac. They will be added to
186 # the flags in VALA_COMPILER_FLAGS and VALA_COMPILER_FLAGS_DEBUG
187 # (for Debug builds) or VALA_COMPILER_FLAGS_RELEASE (for Release
190 # List of dependencies to pass to valac.
192 # Any additional dependencies you would like.
193 macro(vala_precompile_target TARGET GENERATED_SOURCES)
195 set (oneValueArgs VAPI GIR HEADER)
196 set (multiValueArgs FLAGS PACKAGES DEPENDS)
197 cmake_parse_arguments(VALAC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
200 unset (multiValueArgs)
204 set(VALA_OUTPUT_SOURCES)
207 list(APPEND non_source_out_files "${VALAC_VAPI}")
208 list(APPEND non_source_valac_args "--vapi" "${VALAC_VAPI}")
212 list(APPEND non_source_out_files "${VALAC_GIR}")
213 list(APPEND non_source_valac_args
214 "--gir" "${VALAC_GIR}"
215 "--library" "${TARGET}"
216 "--shared-library" "${CMAKE_SHARED_LIBRARY_PREFIX}${TARGET}${CMAKE_SHARED_LIBRARY_SUFFIX}")
220 list(APPEND non_source_out_files "${VALAC_HEADER}")
221 list(APPEND non_source_valac_args --header "${VALAC_HEADER}")
224 # Split up the input files into three lists; one containing vala and
225 # genie sources, one containing VAPIs, and one containing C files
226 # (which may seem a bit silly, but it does open up the possibility
227 # of declaring your source file list once instead of having separate
228 # lists for Vala and C).
229 foreach(source ${VALAC_UNPARSED_ARGUMENTS})
230 _vala_parse_source_file_path("${source}"
234 if("vala" STREQUAL "${type}" OR "gs" STREQUAL "${type}")
235 list(APPEND VALA_SOURCES "${source_full}")
236 elseif("vapi" STREQUAL "${type}")
237 list(APPEND VALA_VAPIS "${source_full}")
239 "c" STREQUAL "${type}" OR
240 "h" STREQUAL "${type}" OR
241 "cpp" STREQUAL "${type}" OR
242 "cxx" STREQUAL "${type}" OR
243 "hpp" STREQUAL "${type}")
244 list(APPEND VALA_OUTPUT_SOURCES "${source_full}")
251 # Set the common flags to pass to every valac invocation.
252 set(VALAFLAGS ${VALAC_FLAGS} ${VALA_VAPIS})
253 foreach(pkg ${VALAC_PACKAGES})
254 list(APPEND VALAFLAGS "--pkg" "${pkg}")
256 list(APPEND VALAFLAGS ${VALA_COMPILER_FLAGS})
257 if (CMAKE_BUILD_TYPE MATCHES "Debug")
258 list(APPEND VALAFLAGS ${VALA_COMPILER_FLAGS_DEBUG})
259 elseif(CMAKE_BUILD_TYPE MATCHES "Release")
260 list(APPEND VALAFLAGS ${VALA_COMPILER_FLAGS_RELEASE})
263 # Where to put the output
264 set(TARGET_DIR "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}")
266 set(FAST_VAPI_STAMPS)
268 # Create fast VAPI targets for each vala source
269 foreach(source ${VALA_SOURCES})
270 _vala_parse_source_file_path("${source}"
271 FAST_VAPI fast_vapi_path)
273 # We need somewhere to put the output…
274 _vala_mkdir_for_file("${TARGET_DIR}/${fast_vapi_path}")
278 OUTPUT "${TARGET_DIR}/${fast_vapi_path}.stamp"
279 BYPRODUCTS "${TARGET_DIR}/${fast_vapi_path}"
284 COMMAND "${VALA_EXECUTABLE}"
287 --fast-vapi "${TARGET_DIR}/${fast_vapi_path}"
289 COMMAND "${CMAKE_COMMAND}" ARGS -E touch "${TARGET_DIR}/${fast_vapi_path}.stamp"
290 COMMENT "Generating fast VAPI ${TARGET_DIR}/${fast_vapi_path}")
292 list(APPEND FAST_VAPI_STAMPS "${TARGET_DIR}/${fast_vapi_path}.stamp")
294 unset(fast_vapi_path)
297 # Create a ${TARGET_DIR}-fast-vapis target which depens on all the fast
298 # vapi stamps. We can use this as a dependency to make sure all
299 # fast-vapis are up to date.
301 OUTPUT "${TARGET_DIR}/fast-vapis.stamp"
302 COMMAND "${CMAKE_COMMAND}" ARGS -E touch "${TARGET_DIR}/fast-vapis.stamp"
306 COMMENT "Generating fast VAPIs for ${TARGET}")
308 add_custom_target("${TARGET}-fast-vapis"
309 DEPENDS "${TARGET_DIR}/fast-vapis.stamp")
311 set(VALA_GENERATED_SOURCE_STAMPS)
313 # Add targets to generate C sources
314 foreach(source ${VALA_SOURCES})
315 _vala_parse_source_file_path("${source}"
316 OUTPUT_PATH output_path
317 OUTPUT_DIR output_dir
318 GENERATED_SOURCE generated_source)
320 set(use_fast_vapi_flags)
321 foreach(src ${VALA_SOURCES})
322 if(NOT "${src}" STREQUAL "${source}")
323 _vala_parse_source_file_path("${src}"
324 FAST_VAPI src_fast_vapi_path)
326 list(APPEND use_fast_vapi_flags --use-fast-vapi "${TARGET_DIR}/${src_fast_vapi_path}")
328 unset(src_fast_vapi_path)
333 OUTPUT "${TARGET_DIR}/${generated_source}.stamp"
334 BYPRODUCTS "${TARGET_DIR}/${generated_source}"
335 COMMAND "${VALA_EXECUTABLE}"
337 -d "${TARGET_DIR}/${output_dir}"
341 ${use_fast_vapi_flags}
342 COMMAND "${CMAKE_COMMAND}" ARGS -E touch "${TARGET_DIR}/${generated_source}.stamp"
344 "${TARGET}-fast-vapis"
347 COMMENT "Generating ${TARGET_DIR}/${generated_source}")
348 unset(use_fast_vapi_flags)
350 list(APPEND VALA_OUTPUT_SOURCES "${TARGET_DIR}/${generated_source}")
351 list(APPEND VALA_GENERATED_SOURCE_STAMPS "${TARGET_DIR}/${generated_source}.stamp")
353 unset(fast_vapi_path)
355 unset(generated_source)
359 OUTPUT "${TARGET_DIR}/stamp"
360 COMMAND "${CMAKE_COMMAND}" ARGS -E touch "${TARGET_DIR}/stamp"
361 DEPENDS ${VALA_GENERATED_SOURCE_STAMPS}
362 COMMENT "Generating sources from Vala for ${TARGET}")
364 set("${GENERATED_SOURCES}" ${VALA_OUTPUT_SOURCES})
366 if(non_source_out_files)
367 set(use_fast_vapi_flags)
368 foreach(source ${VALA_SOURCES})
369 _vala_parse_source_file_path("${source}"
370 FAST_VAPI fast_vapi_path)
372 list(APPEND use_fast_vapi_flags --use-fast-vapi "${TARGET_DIR}/${fast_vapi_path}")
374 unset(fast_vapi_path)
377 add_custom_command(OUTPUT ${non_source_out_files}
378 COMMAND ${VALA_EXECUTABLE}
381 ${non_source_valac_args}
383 ${use_fast_vapi_flags}
385 "${TARGET}-fast-vapis"
387 unset(use_fast_vapi_flags)
390 # CMake doesn't allow file-level dependencies across directories, so
391 # we provide a target we can depend on from other directories.
392 add_custom_target("${TARGET}"
394 "${TARGET_DIR}/stamp"
395 ${non_source_out_files}
397 ${VALA_GENERATED_SOURCE_STAMPS})
399 unset(non_source_out_files)
400 unset(non_source_valac_args)
402 unset(VALA_GENERATED_SOURCE_STAMPS)
403 unset(FAST_VAPI_STAMPS)
408 unset(VALA_OUTPUT_SOURCES)