Fix #5732 - change to cmake for build
[roojspacker] / cmake / FindVala.cmake
1 # FindVala.cmake
2 # <https://github.com/nemequ/gnome-cmake>
3 #
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
9 # different options.
10 #
11 # License:
12 #
13 #   Copyright (c) 2016 Evan Nemerson <evan@nemerson.com>
14 #
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:
22 #
23 #   The above copyright notice and this permission notice shall be
24 #   included in all copies or substantial portions of the Software.
25 #
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.
34
35 set(VALAC_NAMES valac)
36
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")
41 endwhile()
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")
46 endwhile()
47 unset(_FIND_VALA_CURRENT_VERSION)
48
49 find_program(VALA_EXECUTABLE
50   NAMES ${VALAC_NAMES})
51 mark_as_advanced(VALA_EXECUTABLE)
52
53 unset(VALAC_NAMES)
54
55 if(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}")
60
61   add_executable(valac IMPORTED)
62   set_property(TARGET valac PROPERTY IMPORTED_LOCATION "${VALA_EXECUTABLE}")
63 endif(VALA_EXECUTABLE)
64
65 include(FindPackageHandleStandardArgs)
66 find_package_handle_standard_args(Vala
67     REQUIRED_VARS VALA_EXECUTABLE
68     VERSION_VAR VALA_VERSION)
69
70 function(_vala_mkdir_for_file file)
71   get_filename_component(dir "${file}" DIRECTORY)
72   file(MAKE_DIRECTORY "${dir}")
73 endfunction()
74
75 macro(_vala_parse_source_file_path source)
76   set (options)
77   set (oneValueArgs SOURCE TYPE OUTPUT_PATH OUTPUT_DIR GENERATED_SOURCE FAST_VAPI)
78   set (multiValueArgs)
79   cmake_parse_arguments(VALAPATH "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
80   unset (options)
81   unset (oneValueArgs)
82   unset (multiValueArgs)
83
84   if(VALAPATH_SOURCE)
85     get_filename_component("${VALAPATH_SOURCE}" "${source}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
86   endif()
87
88   if(VALAPATH_TYPE)
89     string(REGEX MATCH "[^\\.]+$" "${VALAPATH_TYPE}" "${source}")
90     string(TOLOWER "${${VALAPATH_TYPE}}" "${VALAPATH_TYPE}")
91   endif()
92
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}")
95
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}")
101     else()
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}")
107       else ()
108         # TODO: this probably doesn't work correctly on Windows…
109         set(outpath "root${tmp}")
110       endif()
111     endif()
112
113     unset(tmp)
114     unset(dirlen)
115     unset(srcfile)
116   endif()
117
118   if(VALAPATH_OUTPUT_PATH)
119     set("${VALAPATH_OUTPUT_PATH}" "${outpath}")
120   endif()
121
122   if(VALAPATH_GENERATED_SOURCE)
123     string(REGEX REPLACE "\\.(vala|gs)$" ".c" "${VALAPATH_GENERATED_SOURCE}" "${outpath}")
124   endif()
125
126   if(VALAPATH_FAST_VAPI)
127     string(REGEX REPLACE "\\.(vala|gs)$" ".vapi" "${VALAPATH_FAST_VAPI}" "${outpath}")
128   endif()
129
130   if(VALAPATH_OUTPUT_DIR)
131     get_filename_component("${VALAPATH_OUTPUT_DIR}" "${outpath}" DIRECTORY)
132   endif()
133
134   unset(outpath)
135 endmacro()
136
137 # vala_precompile_target(
138 #   TARGET
139 #   GENERATED_SOURCES
140 #   SOURCES…
141 #   [VAPI vapi-name.vapi]
142 #   [GIR name-version.gir]
143 #   [HEADER name.h]
144 #   [FLAGS …]
145 #   [PACKAGES …]
146 #   [DEPENDS …])
147 #
148 # This function will use valac to generate C code.
149 #
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
157 # directories).
158 #
159 # Options:
160 #
161 #   TARGET
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.
166 #   GENERATED_SOURCES
167 #     Variable in which to store the list of generated sources (which
168 #     you should pass to add_executable or add_library).
169 #   SOURCES
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).
174 #   VAPI name.vapi
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
177 #     name here.
178 #   GIR name-version.gir
179 #     If you would like to have valac generate a GIR, pass the file
180 #     name here.
181 #   HEADER name.h
182 #     If you would like to have valac generate a C header, pass the
183 #     file name here.
184 #   FLAGS …
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
188 #     builds).
189 #   PACKAGES
190 #     List of dependencies to pass to valac.
191 #   DEPENDS
192 #     Any additional dependencies you would like.
193 macro(vala_precompile_target TARGET GENERATED_SOURCES)
194   set (options)
195   set (oneValueArgs VAPI GIR HEADER)
196   set (multiValueArgs FLAGS PACKAGES DEPENDS)
197   cmake_parse_arguments(VALAC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
198   unset (options)
199   unset (oneValueArgs)
200   unset (multiValueArgs)
201
202   set(VALA_SOURCES)
203   set(VALA_VAPIS)
204   set(VALA_OUTPUT_SOURCES)
205
206   if(VALAC_VAPI)
207     list(APPEND non_source_out_files "${VALAC_VAPI}")
208     list(APPEND non_source_valac_args "--vapi" "${VALAC_VAPI}")
209   endif()
210
211   if(VALAC_GIR)
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}")
217   endif()
218
219   if(VALAC_HEADER)
220     list(APPEND non_source_out_files "${VALAC_HEADER}")
221     list(APPEND non_source_valac_args --header "${VALAC_HEADER}")
222   endif()
223
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}"
231       SOURCE source_full
232       TYPE type)
233
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}")
238     elseif(
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}")
245     endif()
246
247     unset(type)
248     unset(source_full)
249   endforeach()
250
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}")
255   endforeach()
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})
261   endif()
262
263   # Where to put the output
264   set(TARGET_DIR "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}")
265
266   set(FAST_VAPI_STAMPS)
267
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)
272
273     # We need somewhere to put the output…
274     _vala_mkdir_for_file("${TARGET_DIR}/${fast_vapi_path}")
275
276     # Create the target
277     add_custom_command(
278       OUTPUT "${TARGET_DIR}/${fast_vapi_path}.stamp"
279       BYPRODUCTS "${TARGET_DIR}/${fast_vapi_path}"
280       DEPENDS
281         "${source}"
282         ${VALA_VAPIS}
283         ${VALAC_DEPENDS}
284       COMMAND "${VALA_EXECUTABLE}"
285       ARGS
286         "${source}"
287         --fast-vapi "${TARGET_DIR}/${fast_vapi_path}"
288         ${VALAFLAGS}
289       COMMAND "${CMAKE_COMMAND}" ARGS -E touch "${TARGET_DIR}/${fast_vapi_path}.stamp"
290       COMMENT "Generating fast VAPI ${TARGET_DIR}/${fast_vapi_path}")
291
292     list(APPEND FAST_VAPI_STAMPS "${TARGET_DIR}/${fast_vapi_path}.stamp")
293
294     unset(fast_vapi_path)
295   endforeach()
296
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.
300   add_custom_command(
301     OUTPUT "${TARGET_DIR}/fast-vapis.stamp"
302     COMMAND "${CMAKE_COMMAND}" ARGS -E touch "${TARGET_DIR}/fast-vapis.stamp"
303     DEPENDS
304       ${FAST_VAPI_STAMPS}
305       ${VALAC_DEPENDS}
306     COMMENT "Generating fast VAPIs for ${TARGET}")
307
308   add_custom_target("${TARGET}-fast-vapis"
309     DEPENDS "${TARGET_DIR}/fast-vapis.stamp")
310
311   set(VALA_GENERATED_SOURCE_STAMPS)
312
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)
319
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)
325
326         list(APPEND use_fast_vapi_flags --use-fast-vapi "${TARGET_DIR}/${src_fast_vapi_path}")
327
328         unset(src_fast_vapi_path)
329       endif()
330     endforeach()
331
332     add_custom_command(
333       OUTPUT "${TARGET_DIR}/${generated_source}.stamp"
334       BYPRODUCTS "${TARGET_DIR}/${generated_source}"
335       COMMAND "${VALA_EXECUTABLE}"
336       ARGS
337         -d "${TARGET_DIR}/${output_dir}"
338         -C
339         "${source}"
340         ${VALAFLAGS}
341         ${use_fast_vapi_flags}
342       COMMAND "${CMAKE_COMMAND}" ARGS -E touch "${TARGET_DIR}/${generated_source}.stamp"
343       DEPENDS
344         "${TARGET}-fast-vapis"
345         "${source}"
346         ${VALA_VAPIS}
347       COMMENT "Generating ${TARGET_DIR}/${generated_source}")
348     unset(use_fast_vapi_flags)
349
350     list(APPEND VALA_OUTPUT_SOURCES "${TARGET_DIR}/${generated_source}")
351     list(APPEND VALA_GENERATED_SOURCE_STAMPS "${TARGET_DIR}/${generated_source}.stamp")
352
353     unset(fast_vapi_path)
354     unset(output_dir)
355     unset(generated_source)
356   endforeach()
357
358   add_custom_command(
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}")
363
364   set("${GENERATED_SOURCES}" ${VALA_OUTPUT_SOURCES})
365
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)
371
372       list(APPEND use_fast_vapi_flags --use-fast-vapi "${TARGET_DIR}/${fast_vapi_path}")
373
374       unset(fast_vapi_path)
375     endforeach()
376
377     add_custom_command(OUTPUT ${non_source_out_files}
378       COMMAND ${VALA_EXECUTABLE}
379       ARGS
380         -C
381         ${non_source_valac_args}
382         ${VALAFLAGS}
383         ${use_fast_vapi_flags}
384       DEPENDS
385         "${TARGET}-fast-vapis"
386         ${VALA_VAPIS})
387     unset(use_fast_vapi_flags)
388   endif()
389
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}"
393     DEPENDS
394       "${TARGET_DIR}/stamp"
395       ${non_source_out_files}
396       ${VALAC_DEPENDS}
397       ${VALA_GENERATED_SOURCE_STAMPS})
398
399   unset(non_source_out_files)
400   unset(non_source_valac_args)
401
402   unset(VALA_GENERATED_SOURCE_STAMPS)
403   unset(FAST_VAPI_STAMPS)
404   unset(TARGET_DIR)
405   unset(VALAFLAGS)
406   unset(VALA_SOURCES)
407   unset(VALA_VAPIS)
408   unset(VALA_OUTPUT_SOURCES)
409 endmacro()