Remove duplication in version info target creation
[alexxy/gromacs.git] / cmake / gmxCustomCommandUtilities.cmake
1 #
2 # This file is part of the GROMACS molecular simulation package.
3 #
4 # Copyright (c) 2014, by the GROMACS development team, led by
5 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 # and including many others, as listed in the AUTHORS file in the
7 # top-level source directory and at http://www.gromacs.org.
8 #
9 # GROMACS is free software; you can redistribute it and/or
10 # modify it under the terms of the GNU Lesser General Public License
11 # as published by the Free Software Foundation; either version 2.1
12 # of the License, or (at your option) any later version.
13 #
14 # GROMACS is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 # Lesser General Public License for more details.
18 #
19 # You should have received a copy of the GNU Lesser General Public
20 # License along with GROMACS; if not, see
21 # http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23 #
24 # If you want to redistribute modifications to GROMACS, please
25 # consider that scientific software is very special. Version
26 # control is crucial - bugs must be traceable. We will be happy to
27 # consider code for inclusion in the official distribution, but
28 # derived work must not be called official GROMACS. Details are found
29 # in the README & COPYING files - if they are missing, get the
30 # official version at http://www.gromacs.org.
31 #
32 # To help us fund GROMACS development, we humbly ask that you cite
33 # the research papers on the package. Check out http://www.gromacs.org.
34
35 # Helper functions for creating custom commands
36 #
37 # CMake semantics of add_custom_command() and add_custom_target() are not
38 # always very convenient or intuitive for creating commands that do not always
39 # run.  This file provides a gmx_add_custom_output_target() to simplify the
40 # task.  The function also provides some convenience features to remove code
41 # duplication in some parts of the GROMACS build system.
42 #
43 # Additionally, gmx_get_stamp_filename() and gmx_get_files_with_gitattribute()
44 # are provided independently to help in creating custom commands.
45
46 # Helper function to create a stamp file name for a target.
47 #
48 # Usage:
49 #   gmx_get_stamp_filename(<variable> <targetname>)
50 #
51 #   <variable>   - name of variable to receive the stamp name
52 #   <targetname> - name of target for which to generate the stamp name
53 #
54 # This is used internally by gmx_add_custom_target(... OUTPUT STAMP ...), but
55 # can also be called directly to create uniform stamp file names throughout the
56 # build system.
57 # <targetname> can be any string that is a part of a valid file name; it does
58 # not need to name an existing or to-be-created target.
59 function (gmx_get_stamp_filename variable targetname)
60     set(_filename "${targetname}")
61     if (NOT "${targetname}" MATCHES "stamp$")
62         set(_filename "${targetname}-timestamp")
63     endif()
64     set(${variable} "${CMAKE_CURRENT_BINARY_DIR}/${_filename}.txt"
65         PARENT_SCOPE)
66 endfunction()
67
68 # Helper function to tell gmx_add_custom_output_target() the name of an output
69 # file for a custom target.
70 #
71 # Usage:
72 #   gmx_set_custom_target_output(<targetname> <output>)
73 #
74 #   <targetname> - name of an existing custom target
75 #   <output>     - path to the output file produced by the custom target
76 #
77 # This is used internally by gmx_add_custom_output_target(), but can also be
78 # called for targets created with add_custom_target() to make them work with
79 # the dependency resolution mechanism in gmx_add_custom_output_target() if
80 # those targets for some reason cannot be created with that command.
81 function (gmx_set_custom_target_output targetname output)
82     # Store the output file name in a custom property to be used in dependency
83     # resolution later.
84     if (NOT IS_ABSOLUTE ${output})
85         set(output ${CMAKE_CURRENT_BINARY_DIR}/${output})
86     endif()
87     set_property(TARGET ${targetname}
88         PROPERTY GMX_CUSTOM_TARGET_OUTPUT_FILE ${output})
89 endfunction()
90
91 # More flexible alternative to add_custom_command() and add_custom_target()
92 # for dependent custom commands.  It adds a few convenience features:
93 #   - Support for custom commands that always run (like add_custom_target()),
94 #     but still have the ability to act as dependencies of other custom
95 #     commands (such that the dependent commands run only if the output
96 #     has been updated) also for Ninja.
97 #   - Adds file-level dependencies between custom targets added with this
98 #     command such that if there is a target-level dependency, it also implies
99 #     that the custom command should always be run if the output file of the
100 #     dependency has been updated.
101 #   - Support for creating custom targets that produce stamp files whenever
102 #     they run successfully, so that other targets can depend on those stamp
103 #     files.
104 #
105 # Usage:
106 #   gmx_add_custom_output_target(<target> [RUN_ALWAYS] [ADD_FAST_TARGET]
107 #                                OUTPUT <STAMP | <output> >
108 #                                [COMMAND <command1> [<args1...>]]
109 #                                [COMMAND <command2> [<args2...>]]
110 #                                [WORKING_DIRECTORY <dir>]
111 #                                [DEPENDS <deps...>]
112 #                                [DEPENDS_FILE_LIST <list>]
113 #                                [COMMENT <comment>])
114 #
115 #   <target>
116 #     - Name of the custom target to create.
117 #   RUN_ALWAYS
118 #     - Create the command such that it always runs.
119 #       This takes care of differences between the Ninja generator and others,
120 #       which require different rules to make this happen such that
121 #       dependencies on the output of the target work correctly, also in the
122 #       case the command does not always update the timestamp of the output.
123 #       The dependencies listed with DEPENDS are ignored in this case.
124 #   ADD_FAST_TARGET
125 #     - In addition to creating <target>, create a secondary target
126 #       <target>-fast that always runs the same commands, but does not have
127 #       any dependencies.  Desired dependencies can be added separately using
128 #       add_dependencies().  This supports cases where some of the dependencies
129 #       are time-consuming to build, and it is possible to build the target
130 #       even without up-to-date dependencies for testing only that part of the
131 #       build.
132 #   OUTPUT
133 #     - Sets the name of the output file from this custom target.
134 #       Can be set to STAMP, in which case a stamp file name is automatically
135 #       generated and commands to touch that stamp whenever the target is made
136 #       are added.
137 #   COMMAND
138 #     - Passed to add_custom_command()/add_custom_target().
139 #       If OUTPUT STAMP is used, then a command to touch the stamp is
140 #       automatically added.  In this case, it is allowed to not specify any
141 #       commands explicitly.
142 #   WORKING_DIRECTORY
143 #     - Passed to add_custom_command()/add_custom_target()
144 #   COMMENT
145 #     - Passed to add_custom_command()/add_custom_target()
146 #   DEPENDS
147 #     - Dependencies passed to add_custom_command().  Any targets in this list
148 #       that have been created with gmx_add_custom_output_target() are
149 #       automatically expanded such that the custom command always runs if the
150 #       output of the dependent target changes.  It is not necessary to list
151 #       those output files here explicitly.
152 #   DEPENDS_FILE_LIST
153 #     - Names of variables from which dependencies are added verbatim.
154 #       The target expansion described above is not performed for these
155 #       dependencies, and the value passed to the function is the name of a
156 #       list variable, not the list itself.  This provides much better
157 #       performance if the dependency list is a long list of source files.
158 #
159 # This function does not need a VERBATIM argument; that is always used when
160 # creating the underlying custom commands/targets.
161 function (gmx_add_custom_output_target targetname)
162     # Parse the arguments
163     # CMakeParseArguments is not suitable, since it does not support the use of
164     # multiple COMMAND parameters that add_custom_target/command() supports.
165     set(_option "")
166     set(_command_args "")
167     set(_deps "")
168     set(_output "")
169     set(_stamp OFF)
170     set(_always OFF)
171     set(_add_fast OFF)
172     foreach (_arg ${ARGN})
173         if ("x${_arg}" STREQUAL "xRUN_ALWAYS")
174             set(_always ON)
175         elseif ("x${_arg}" STREQUAL "xADD_FAST_TARGET")
176             set(_add_fast ON)
177         elseif ("x${_arg}" MATCHES "^x(OUTPUT|DEPENDS|DEPENDS_FILE_LIST)$")
178             set(_option ${_arg})
179         elseif ("x${_arg}" MATCHES "^x(COMMAND|COMMENT|WORKING_DIRECTORY)$")
180             set(_option "PASS")
181             list(APPEND _command_args "${_arg}")
182         elseif ("${_option}" STREQUAL "DEPENDS")
183             list(APPEND _deps "${_arg}")
184             # If the dependency is a target created with this command, also add
185             # the output file as a dependency.
186             if (TARGET "${_arg}")
187                 get_property(_target_output
188                     TARGET "${_arg}" PROPERTY GMX_CUSTOM_TARGET_OUTPUT_FILE)
189                 if (_target_output)
190                     list(APPEND _deps ${_target_output})
191                 endif()
192             endif()
193         elseif ("${_option}" STREQUAL "PASS")
194             list(APPEND _command_args "${_arg}")
195         elseif ("${_option}" STREQUAL "DEPENDS_FILE_LIST")
196             list(APPEND _deps ${${_arg}})
197         elseif ("${_option}" STREQUAL "OUTPUT")
198             if (_output)
199                 message(FATAL_ERROR "Multiple OUTPUTs not supported")
200             endif()
201             if ("${_arg}" STREQUAL "STAMP")
202                 gmx_get_stamp_filename(_output ${targetname})
203                 set(_stamp ON)
204             else()
205                 set(_output ${_arg})
206             endif()
207         else()
208             message(FATAL_ERROR "Unknown option ${_arg}")
209         endif()
210     endforeach()
211     # Add automatically a command to update the stamp if requested
212     if (_stamp)
213         list(APPEND _command_args COMMAND ${CMAKE_COMMAND} -E touch ${_output})
214     endif()
215     # Create the actual command as requested.
216     if (NOT _always)
217         # If the command does not need to run always, the standard CMake
218         # mechanism is sufficient.
219         add_custom_command(OUTPUT ${_output}
220             ${_command_args} DEPENDS ${_deps} VERBATIM)
221         add_custom_target(${targetname} DEPENDS ${_output})
222     elseif (CMAKE_GENERATOR STREQUAL "Ninja")
223         # Ninja requires all generated files mentioned in dependencies of custom
224         # commands to be actually mentioned in the build system, and luckily
225         # add_custom_command() makes that possible.
226         # But it seems impossible to create a robust custom command that would be
227         # always run, so other generators that do not have this constraint simply
228         # use an add_custom_target().
229         #
230         # The second, phony file is never created, so the rule is always
231         # triggered again.  TODO: Figure out why this works, even though ninja
232         # very eagerly complains about missing files.
233         # This unfortunately does not work with the make generator, as
234         # the non-existent second file causes some part of the generated system
235         # erase the first file at the beginning of every build, causing a full
236         # rebuild of the dependencies.
237         add_custom_command(OUTPUT ${_output} ${targetname}-phony
238             ${_command_args} VERBATIM)
239         # The generated Ninja build system would probably work fine even
240         # without this target, but CMake requires all custom commands to belong
241         # to a target in the same CMakeLists.txt to generate anything for them.
242         add_custom_target(${targetname} DEPENDS ${_output})
243     else()
244         # For other generators, a target-level dependency on the custom target
245         # ensures that the output is created before the dependent targets'
246         # dependencies are even evaluated.
247         add_custom_target(${targetname} ${_command_args} VERBATIM)
248     endif()
249     # Store the output file name in a custom property to be used in dependency
250     # resolution later.
251     gmx_set_custom_target_output(${targetname} ${_output})
252     # Create the fast target if requested.
253     if (_add_fast)
254         add_custom_target(${targetname}-fast ${_command_args} VERBATIM)
255     endif()
256 endfunction()
257
258 # Gets list of files in the source tree with the given git attribute set
259 #
260 # Usage:
261 #   gmx_get_files_with_gitattribute(<variable> <attribute>)
262 #
263 #   <variable>  - name of variable to receive the file list
264 #   <attribute> - name of git attribute to use
265 #
266 # This is useful to generate a list of dependencies for custom commands when
267 # there is a long list of files that cannot be easily just globbed.
268 # Using git attributes allows keeping complicated logic out of the build
269 # system, as expressing such logic in a CMake script that would be reasonably
270 # fast to evaluate for a long file list is convenient or easy.
271 function (gmx_get_files_with_gitattribute variable attribute)
272     execute_process(
273         COMMAND ${GIT_EXECUTABLE} ls-files
274         COMMAND ${GIT_EXECUTABLE} check-attr ${attribute} --stdin
275         WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
276         OUTPUT_VARIABLE _files)
277     string(REGEX MATCHALL "[^\n]*: ${attribute}: set\n"
278            _files_with_attr "${_files}")
279     string(REGEX REPLACE "([^;]*): ${attribute}: set\n" "${PROJECT_SOURCE_DIR}/\\1"
280            _files "${_files_with_attr}")
281     set(${variable} ${_files} PARENT_SCOPE)
282 endfunction()