1# @HEADER
2# ************************************************************************
3#
4#            TriBITS: Tribal Build, Integrate, and Test System
5#                    Copyright 2013 Sandia Corporation
6#
7# Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
8# the U.S. Government retains certain rights in this software.
9#
10# Redistribution and use in source and binary forms, with or without
11# modification, are permitted provided that the following conditions are
12# met:
13#
14# 1. Redistributions of source code must retain the above copyright
15# notice, this list of conditions and the following disclaimer.
16#
17# 2. Redistributions in binary form must reproduce the above copyright
18# notice, this list of conditions and the following disclaimer in the
19# documentation and/or other materials provided with the distribution.
20#
21# 3. Neither the name of the Corporation nor the names of the
22# contributors may be used to endorse or promote products derived from
23# this software without specific prior written permission.
24#
25# THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
26# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
29# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36#
37# ************************************************************************
38# @HEADER
39
40INCLUDE(CMakeParseArguments)
41
42
43#
44# @FUNCTION: TRIBITS_FIND_MOST_RECENT_FILE_TIMESTAMP()
45#
46# Find the most modified file in a set of base directories and return its
47# timestamp.
48#
49# Usage::
50#
51#   TRIBITS_FIND_MOST_RECENT_FILE_TIMESTAMP(
52#     BASE_DIRS <dir0> <dir1> ...
53#     [BASE_BASE_DIR <dir>]
54#     [EXCLUDE_REGEXES "<re0>" "<re1>" ...
55#     [SHOW_MOST_RECENT_FILES]
56#     [SHOW_OVERALL_MOST_RECENT_FILE]
57#     [MOST_RECENT_TIMESTAMP_OUT  <mostRecentTimestamp>]
58#     [MOST_RECENT_FILEPATH_BASE_DIR_OUT <mostRecentFilepathBaseDir>]
59#     [MOST_RECENT_RELATIVE_FILEPATH_OUT <mostRecentRelativeFilePath>]
60#     )
61#
62# **Arguments:**
63#
64#   ``BASE_DIRS <dir0> <dir1> ...``
65#
66#     Gives the absolute base directory paths that will be searched for the
67#     most recently modified files, as described above.
68#
69#   ``BASE_BASE_DIR <dir>```
70#
71#     Absolute path for which to print file paths relative to.  This makes
72#     outputting less verbose and easier to read (optional).
73#
74#   ``EXCLUDE_REGEXES "<re0>" "<re1>" ...``
75#
76#     Gives the regular expressions that are used to exclude files from
77#     consideration.  Each "<rei>" regex is used with a `grep -v "<rei>"`
78#     filter to exclude files before sorting by time stamp.
79#
80#   ``SHOW_MOST_RECENT_FILES``
81#
82#     If specified, then the most recently modified file for each individual
83#     directory ``<dir0>``, ``<dir1``, ... will be printed the STDOUT.
84#     Setting this implies ``SHOW_OVERALL_MOST_RECENT_FILE``.
85#
86#   ``SHOW_OVERALL_MOST_RECENT_FILE``
87#
88#     If specified, then only the most recent modified file over all of the
89#     individual directories is printed to STDOUT.
90#
91#   ``MOST_RECENT_TIMESTAMP_OUT <mostRecentTimestamp>``
92#
93#      On output, the variable `<mostRecentTimestamp>` is set that gives the
94#      timestamp of the most recently modified file over all the directories.
95#      This number is given as the number of seconds since Jan. 1, 1970, 00:00
96#      GMT.
97#
98#   ``MOST_RECENT_FILEPATH_BASE_DIR_OUT <mostRecentFilepathBaseDir>``
99#
100#     On output, the variable `<mostRecentFilepathBaseDir>` gives absolute base
101#     directory of the file with the most recent timestamp over all
102#     directories.
103#
104#   ``MOST_RECENT_RELATIVE_FILEPATH_OUT <mostRecentRelativeFilePath>``
105#
106#     On output, the variable `<mostRecentFilepathBaseDir>` gives the file
107#     name with relative path to the file with the most recent timestamp over
108#     all directories.
109#
110# **Description:**
111#
112# This function uses the Linux/Unix command::
113#
114#     $ find . -type f -printf '%T@ %p\n' \
115#         | grep -v "<re0>" | grep -v "<re1>" | ... \
116#         | sort -n | tail -1
117#
118# to return the most recent file in each listed directory ``<dir0>``,
119# ``<dir1>``, etc.  It then determines the most recently modified file over
120# all of the directories and prints and returns in the variables
121# ``<mostRecentTimestamp>``, ``<mostRecentFilepathBaseDir>``, and
122# ``<mostRecentRelativeFilePath>``.
123#
124FUNCTION(TRIBITS_FIND_MOST_RECENT_FILE_TIMESTAMP)
125
126    IF (TRIBITS_FIND_MOST_RECENT_FILE_TIMESTAMP_DEBUG_DUMP)
127      MESSAGE("\nSearching for most modified files in base dirs:")
128    ENDIF()
129
130  #
131  # A) Parse the input arguments
132  #
133
134  CMAKE_PARSE_ARGUMENTS(
135     #prefix
136     PARSE
137     #options
138     "SHOW_MOST_RECENT_FILES;SHOW_OVERALL_MOST_RECENT_FILES"
139     #one_value_keywords
140     ""
141     #mulit_value_keywords
142     "BASE_DIRS;BASE_BASE_DIR;EXCLUDE_REGEXES;MOST_RECENT_TIMESTAMP_OUT;MOST_RECENT_FILEPATH_BASE_DIR_OUT;MOST_RECENT_RELATIVE_FILEPATH_OUT"
143     ${ARGN}
144     )
145
146  TRIBITS_CHECK_FOR_UNPARSED_ARGUMENTS()
147
148  IF (PARSE_SHOW_MOST_RECENT_FILES)
149    SET(PARSE_SHOW_OVERALL_MOST_RECENT_FILE ON)
150  ENDIF()
151
152  #
153  # B) Loop over each directory and find the most modified file
154  #
155
156  SET(OVERALL_MOST_RECENT_TIMESTAMP "0000000000.0000000000")
157  SET(OVERALL_MOST_RECENT_FILEPATH "")
158  SET(OVERALL_MOST_RECENT_FILEPATH_DIR "")
159  SET(OVERALL_MOST_RECENT_RELATEIVE_FILEPATH_DIR "")
160  SET(OVERALL_MOST_RECENT_FILEPATH_TIMESTAMP_HUMAN_READABLE "")
161
162  FOREACH(BASE_DIR ${PARSE_BASE_DIRS})
163
164    IF (TRIBITS_FIND_MOST_RECENT_FILE_TIMESTAMP_DEBUG_DUMP)
165      MESSAGE("\nSearching '${BASE_DIR}' ...")
166    ENDIF()
167
168    IF (IS_DIRECTORY "${BASE_DIR}")
169
170      # Build up commands for grep -v
171      SET(GREP_V_COMMANDS)
172      FOREACH(EXCLUDE_REGEX ${PARSE_EXCLUDE_REGEXES})
173        APPEND_SET(GREP_V_COMMANDS COMMAND grep -v "${EXCLUDE_REGEX}")
174      ENDFOREACH()
175
176      # Get the time stamp and the file name of the most recently modified file
177      # in currnet directory.
178      EXECUTE_PROCESS(
179        WORKING_DIRECTORY "${BASE_DIR}"
180        COMMAND find . -type f -printf "%T@ %p\n"
181        ${GREP_V_COMMANDS}
182        COMMAND sort -n
183        COMMAND tail -1
184        OUTPUT_VARIABLE MOST_RECENT_TIMESTAMP_AND_FILE
185        OUTPUT_STRIP_TRAILING_WHITESPACE
186        )
187       # Here, note that %T@ gives the modification time stamp in seconds since
188       # Jan. 1, 1970, 00:00 GMT.  The -printf argument %p gives the file path.
189       # This results in the return a string with the modification date (in
190       # fractional seconds) and the file name of the form:
191       #
192       #     1407353359.5651538200 ./<relative-dir>/<some-file-name>
193
194      IF (TRIBITS_FIND_MOST_RECENT_FILE_TIMESTAMP_DEBUG_DUMP)
195        PRINT_VAR(MOST_RECENT_TIMESTAMP_AND_FILE)
196      ENDIF()
197
198      IF (MOST_RECENT_TIMESTAMP_AND_FILE)
199
200        SPLIT("${MOST_RECENT_TIMESTAMP_AND_FILE}" " "
201          MOST_RECENT_TIMESTAMP_AND_FILE_SPLIT)
202
203        # Get the time stamp part
204        LIST(GET MOST_RECENT_TIMESTAMP_AND_FILE_SPLIT 0
205          CURRENT_TIMESTAMP)
206
207        # Get the relative file path
208        LIST(GET MOST_RECENT_TIMESTAMP_AND_FILE_SPLIT 1
209          CURRENT_FILEPATH)
210
211        # Get the directory relative to the base base dir
212        STRING(REPLACE "${PARSE_BASE_BASE_DIR}/" "./"  RELATIVE_FILEPATH_DIR
213          "${BASE_DIR}")
214
215        IF (TRIBITS_FIND_MOST_RECENT_FILE_TIMESTAMP_DEBUG_DUMP)
216          PRINT_VAR(CURRENT_TIMESTAMP)
217          PRINT_VAR(CURRENT_FILEPATH)
218        ENDIF()
219
220        IF (PARSE_SHOW_MOST_RECENT_FILES)
221          TRIBITS_GET_HUMAN_READABLE_FILE_AND_TIMESTAMP(
222            "${BASE_DIR}"  "${CURRENT_FILEPATH}"
223            HUMAN_READABLE_FILE_AND_TIMESTAMP
224            )
225          MESSAGE("-- " "Most recent file in ${RELATIVE_FILEPATH_DIR}/"
226            " is ${CURRENT_FILEPATH}\n"
227            "    ${HUMAN_READABLE_FILE_AND_TIMESTAMP}")
228        ENDIF()
229
230        IF ("${CURRENT_TIMESTAMP}" GREATER "${OVERALL_MOST_RECENT_TIMESTAMP}")
231          IF (TRIBITS_FIND_MOST_RECENT_FILE_TIMESTAMP_DEBUG_DUMP)
232            MESSAGE("    New most recent file path!")
233          ENDIF()
234          SET(OVERALL_MOST_RECENT_TIMESTAMP "${CURRENT_TIMESTAMP}")
235          SET(OVERALL_MOST_RECENT_RELATIVE_FILEPATH "${CURRENT_FILEPATH}")
236          SET(OVERALL_MOST_RECENT_FILEPATH_DIR "${BASE_DIR}")
237          SET(OVERALL_MOST_RECENT_RELATEIVE_FILEPATH_DIR "${RELATIVE_FILEPATH_DIR}")
238          IF (PARSE_SHOW_OVERALL_MOST_RECENT_FILES)
239            SET(OVERALL_MOST_RECENT_FILEPATH_TIMESTAMP_HUMAN_READABLE
240              "${HUMAN_READABLE_FILE_AND_TIMESTAMP}")
241          ENDIF()
242        ENDIF()
243
244      ENDIF()
245
246    ELSE()
247
248      IF (TRIBITS_FIND_MOST_RECENT_FILE_TIMESTAMP_DEBUG_DUMP)
249        MESSAGE("Directory does not exist, skipping ...")
250      ENDIF()
251
252    ENDIF()
253
254  ENDFOREACH()
255
256  IF (TRIBITS_FIND_MOST_RECENT_FILE_TIMESTAMP_DEBUG_DUMP)
257    PRINT_VAR(OVERALL_MOST_RECENT_TIMESTAMP)
258    PRINT_VAR(OVERALL_MOST_RECENT_RELATIVE_FILEPATH)
259  ENDIF()
260
261  IF (PARSE_SHOW_OVERALL_MOST_RECENT_FILES)
262    IF (OVERALL_MOST_RECENT_FILEPATH_DIR)
263      IF (NOT OVERALL_MOST_RECENT_FILEPATH_TIMESTAMP_HUMAN_READABLE)
264        TRIBITS_GET_HUMAN_READABLE_FILE_AND_TIMESTAMP(
265          "${OVERALL_MOST_RECENT_FILEPATH_DIR}"
266          "${OVERALL_MOST_RECENT_RELATIVE_FILEPATH}"
267          OVERALL_MOST_RECENT_FILEPATH_TIMESTAMP_HUMAN_READABLE
268        )
269      ENDIF()
270      MESSAGE("-- " "Overall most recent modified file is in"
271        " ${OVERALL_MOST_RECENT_RELATEIVE_FILEPATH_DIR}/ and is ${OVERALL_MOST_RECENT_RELATIVE_FILEPATH}\n"
272        "    ${OVERALL_MOST_RECENT_FILEPATH_TIMESTAMP_HUMAN_READABLE}")
273    ELSE()
274      MESSAGE("-- There are no unfiltered files!")
275    ENDIF()
276  ENDIF()
277
278  SET(${PARSE_MOST_RECENT_TIMESTAMP_OUT}
279    ${OVERALL_MOST_RECENT_TIMESTAMP}
280    PARENT_SCOPE)
281
282  IF (PARSE_MOST_RECENT_FILEPATH_BASE_DIR_OUT)
283    SET(${PARSE_MOST_RECENT_FILEPATH_BASE_DIR_OUT}
284      ${OVERALL_MOST_RECENT_FILEPATH_DIR}
285      PARENT_SCOPE)
286  ENDIF()
287
288  IF (PARSE_MOST_RECENT_RELATIVE_FILEPATH_OUT)
289    SET(${PARSE_MOST_RECENT_RELATIVE_FILEPATH_OUT}
290      ${OVERALL_MOST_RECENT_RELATIVE_FILEPATH}
291      PARENT_SCOPE )
292  ENDIF()
293
294ENDFUNCTION()
295
296
297#
298# @FUNCTION: TRIBITS_FIND_MOST_RECENT_SOURCE_FILE_TIMESTAMP()
299#
300# Find the most modified source file in a set of base directories and return
301# its timestamp.
302#
303# Usage::
304#
305#   TRIBITS_FIND_MOST_RECENT_SOURCE_FILE_TIMESTAMP(
306#     SOURCE_BASE_DIRS <dir0> <dir1> ...
307#     [SOURCE_BASE_BASE_DIR <dir>]
308#     [SHOW_MOST_RECENT_FILES]
309#     [SHOW_OVERALL_MOST_RECENT_FILE]
310#     [MOST_RECENT_TIMESTAMP_OUT  <mostRecentTimestamp>]
311#     [MOST_RECENT_FILEPATH_BASE_DIR_OUT <mostRecentFilepathBaseDir>]
312#     [MOST_RECENT_RELATIVE_FILEPATH_OUT <mostRecentRelativeFilePath>]
313#     )
314#
315# This function just calls `TRIBITS_FIND_MOST_RECENT_FILE_TIMESTAMP()`_
316# passing in a set of basic exclude regexes like ``[.]git/``, ``[.]svn/``,
317# etc.  These types of version control files can not possibly directly impact
318# the source code.
319#
320FUNCTION(TRIBITS_FIND_MOST_RECENT_SOURCE_FILE_TIMESTAMP)
321
322  #
323  # A) Parse the input arguments
324  #
325
326  CMAKE_PARSE_ARGUMENTS(
327     #prefix
328     PARSE
329     #options
330     "SHOW_MOST_RECENT_FILES;SHOW_OVERALL_MOST_RECENT_FILES"
331     #one_value_keywords
332     ""
333     #multi_value_keywords
334     "SOURCE_BASE_DIRS;SOURCE_BASE_BASE_DIR;MOST_RECENT_TIMESTAMP_OUT;MOST_RECENT_FILEPATH_BASE_DIR_OUT;MOST_RECENT_RELATIVE_FILEPATH_OUT"
335     ${ARGN}
336     )
337
338  TRIBITS_CHECK_FOR_UNPARSED_ARGUMENTS()
339
340  #
341  # B) Call the function TRIBITS_FIND_MOST_RECENT_FILE_TIMESTAMP()
342  #
343
344  SET(FILTER_OUT_SOURCE_FILE_REGEXS
345    "/[.]git/"
346    )
347
348  SET(VARIABLE_ARGS)
349  IF (PARSE_SHOW_MOST_RECENT_FILES)
350    APPEND_SET(VARIABLE_ARGS SHOW_MOST_RECENT_FILES)
351  ENDIF()
352  IF (PARSE_SHOW_OVERALL_MOST_RECENT_FILES)
353    APPEND_SET(VARIABLE_ARGS SHOW_OVERALL_MOST_RECENT_FILES)
354  ENDIF()
355
356  #PRINT_VAR(VARIABLE_ARGS)
357  TRIBITS_FIND_MOST_RECENT_FILE_TIMESTAMP(
358    BASE_DIRS ${PARSE_SOURCE_BASE_DIRS}
359    BASE_BASE_DIR ${PARSE_SOURCE_BASE_BASE_DIR}
360    EXCLUDE_REGEXES ${FILTER_OUT_SOURCE_FILE_REGEXS}
361    MOST_RECENT_TIMESTAMP_OUT MOST_RECENT_TIMESTAMP
362    MOST_RECENT_FILEPATH_BASE_DIR_OUT  MOST_RECENT_UPSTREAM_SOURCE_TIMESTAMP
363    MOST_RECENT_RELATIVE_FILEPATH_OUT  MOST_RECENT_RELATIVE_FILEPATH
364    ${VARIABLE_ARGS}
365    )
366  #PRINT_VAR(MOST_RECENT_TIMESTAMP)
367
368  SET(${PARSE_MOST_RECENT_TIMESTAMP_OUT} ${MOST_RECENT_TIMESTAMP}
369    PARENT_SCOPE)
370
371  IF (PARSE_MOST_RECENT_FILEPATH_BASE_DIR_OUT)
372    SET(${PARSE_MOST_RECENT_FILEPATH_BASE_DIR_OUT} ${MOST_RECENT_UPSTREAM_SOURCE_TIMESTAMP}
373      PARENT_SCOPE)
374  ENDIF()
375
376  IF (PARSE_MOST_RECENT_RELATIVE_FILEPATH_OUT)
377    SET(${PARSE_MOST_RECENT_RELATIVE_FILEPATH_OUT} ${MOST_RECENT_RELATIVE_FILEPATH}
378      PARENT_SCOPE )
379  ENDIF()
380
381ENDFUNCTION()
382
383
384#
385# @FUNCTION: TRIBITS_FIND_MOST_RECENT_BINARY_FILE_TIMESTAMP()
386#
387# Find the most modified binary file in a set of base directories and return
388# its timestamp.
389#
390# Usage::
391#
392#   TRIBITS_FIND_MOST_RECENT_BINARY_FILE_TIMESTAMP(
393#     BINARY_BASE_DIRS <dir0> <dir1> ...
394#     [BINARY_BASE_BASE_DIR <dir>]
395#     [MOST_RECENT_TIMESTAMP_OUT  <mostRecentTimestamp>]
396#     [MOST_RECENT_FILEPATH_BASE_DIR_OUT <mostRecentFilepathBaseDir>]
397#     [MOST_RECENT_RELATIVE_FILEPATH_OUT <mostRecentRelativeFilePath>]
398#     [SHOW_MOST_RECENT_FILES]
399#     [SHOW_OVERALL_MOST_RECENT_FILE]
400#     )
401#
402# This function just calls `TRIBITS_FIND_MOST_RECENT_FILE_TIMESTAMP()`_
403# passing in a set of basic exclude regexes like ``CMakeFiles/``,
404# ``[.]cmake$``, and ``/Makefile$``, etc.  These types of files usually don't
405# impact the build of downstream software in CMake projects.
406#
407FUNCTION(TRIBITS_FIND_MOST_RECENT_BINARY_FILE_TIMESTAMP)
408
409  #
410  # A) Parse the input arguments
411  #
412
413  CMAKE_PARSE_ARGUMENTS(
414     #prefix
415     PARSE
416     #options
417     "SHOW_MOST_RECENT_FILES;SHOW_OVERALL_MOST_RECENT_FILES"
418     #one_value_keywords
419     ""
420     #multi_value_keywords
421     "BINARY_BASE_DIRS;BINARY_BASE_BASE_DIR;MOST_RECENT_TIMESTAMP_OUT;MOST_RECENT_FILEPATH_BASE_DIR_OUT;MOST_RECENT_RELATIVE_FILEPATH_OUT"
422     ${ARGN}
423     )
424
425  TRIBITS_CHECK_FOR_UNPARSED_ARGUMENTS()
426
427  #
428  # B) Define filters for binary files we know are not significant
429  #
430
431  SET(FILTER_OUT_BINARY_FILE_REGEXS
432    "CMakeFiles/" "[.]cmake$" "/Makefile$"
433    )
434
435  #
436  # C) Call the function TRIBITS_FIND_MOST_RECENT_FILE_TIMESTAMP()
437  #
438
439  SET(VARIABLE_ARGS)
440  IF (PARSE_SHOW_MOST_RECENT_FILES)
441    APPEND_SET(VARIABLE_ARGS SHOW_MOST_RECENT_FILES)
442  ENDIF()
443  IF (PARSE_SHOW_OVERALL_MOST_RECENT_FILES)
444    APPEND_SET(VARIABLE_ARGS SHOW_OVERALL_MOST_RECENT_FILES)
445  ENDIF()
446
447  #PRINT_VAR(VARIABLE_ARGS)
448  TRIBITS_FIND_MOST_RECENT_FILE_TIMESTAMP(
449    BASE_DIRS ${PARSE_BINARY_BASE_DIRS}
450    BASE_BASE_DIR ${PARSE_BINARY_BASE_BASE_DIR}
451    EXCLUDE_REGEXES ${FILTER_OUT_BINARY_FILE_REGEXS}
452    MOST_RECENT_TIMESTAMP_OUT  MOST_RECENT_TIMESTAMP
453    MOST_RECENT_RELATIVE_FILEPATH_OUT  MOST_RECENT_RELATIVE_FILEPATH
454    ${VARIABLE_ARGS}
455    )
456  #PRINT_VAR(MOST_RECENT_TIMESTAMP)
457
458  SET(${PARSE_MOST_RECENT_TIMESTAMP_OUT} ${MOST_RECENT_TIMESTAMP}
459    PARENT_SCOPE)
460
461  IF (PARSE_MOST_RECENT_FILEPATH_BASE_DIR_OUT)
462    SET(${PARSE_MOST_RECENT_FILEPATH_BASE_DIR_OUT} ${MOST_RECENT_UPSTREAM_SOURCE_TIMESTAMP}
463      PARENT_SCOPE)
464  ENDIF()
465
466  IF (PARSE_MOST_RECENT_RELATIVE_FILEPATH_OUT)
467    SET(${PARSE_MOST_RECENT_RELATIVE_FILEPATH_OUT} ${MOST_RECENT_RELATIVE_FILEPATH}
468      PARENT_SCOPE )
469  ENDIF()
470
471ENDFUNCTION()
472
473
474#
475# @FUNCTION: TRIBITS_DETERMINE_IF_CURRENT_PACKAGE_NEEDS_REBUILT()
476#
477# Determine at configure time if any of the upstream dependencies for a
478# package require the current package to be rebuilt.
479#
480# Usage::
481#
482#   TRIBITS_DETERMINE_IF_CURRENT_PACKAGE_NEEDS_REBUILT(
483#     [SHOW_MOST_RECENT_FILES]
484#     [SHOW_OVERALL_MOST_RECENT_FILES]
485#     CURRENT_PACKAGE_OUT_OF_DATE_OUT <currentPackageOutOfDate>
486#     )
487#
488# **Arguments:**
489#
490#   ``SHOW_MOST_RECENT_FILES``
491#
492#     If specified, then the most recently modified file for each individual
493#     base source and binary directory searched will be will be printed the
494#     STDOUT.  Setting this implies ``SHOW_OVERALL_MOST_RECENT_FILE``.
495#
496#   ``SHOW_OVERALL_MOST_RECENT_FILE``
497#
498#     If specified, then only the most recent modified file over all of the
499#     individual directories for each category (i.e. one for upstream SE
500#     package source dirs, one for upstream SE package binary dirs, one for
501#     the package's source dir, and one for the package's own binary dir) is
502#     printed to STDOUT.
503#
504#   ``CURRENT_PACKAGE_OUT_OF_DATE_OUT <currentPackageOutOfDate>``
505#
506#     On output, the local variable ``<currentPackageOutOfDate>`` will be set
507#     to ``TRUE`` if any of the upstream most modified files are more recent
508#     than the most modified file in the package's binary directory.
509#     Otherwise, this variable is set to ``FALSE``.
510#
511# **Description:**
512#
513# This function is designed to help take an externally configured and built
514# piece of software (that generates libraries) and wrap it as a TriBITS
515# package or subpackage.  This function uses the lower-level functions:
516#
517# * `TRIBITS_FIND_MOST_RECENT_SOURCE_FILE_TIMESTAMP()`_
518# * `TRIBITS_FIND_MOST_RECENT_BINARY_FILE_TIMESTAMP()`_
519#
520# to determine the most recent modified files in the upstream TriBITS SE
521# packages' source and binary directories as well as the most recent source
522# file for the current package.  It then compares these timestamps to the most
523# recent binary file timestamp in this package's binary directory.  If any of
524# these three files are more recent than this package's most recent binary
525# file, then the output variable ``<currentPackageOutOfDate>`` is set to
526# ``TRUE``.  Otherwise, it is set to ``FALSE``.
527#
528# NOTE: The source and binary directories for full packages are searched, not
529# individual subpackage dirs.  This is to reduce the number of dirs searched.
530# This will, however, result in changes in non-dependent subpackages being
531# considered as well.
532#
533# See the demonstration of the usage of this function in the ``WrapExternal``
534# package in `TribitsExampleProject`_.
535#
536FUNCTION(TRIBITS_DETERMINE_IF_CURRENT_PACKAGE_NEEDS_REBUILT)
537
538  IF (${PROJECT_NAME}_ENABLE_CONFIGURE_TIMING)
539    TIMER_GET_RAW_SECONDS(TIMER_START_SECONDS)
540  ENDIF()
541
542
543  #
544  # A) Parse the input arguments
545  #
546
547  CMAKE_PARSE_ARGUMENTS(
548     #prefix
549     PARSE
550     #options
551     "SHOW_MOST_RECENT_FILES;SHOW_OVERALL_MOST_RECENT_FILES"
552     #one_value_keywords
553     ""
554     #mulit_value_keywords
555     "CURRENT_PACKAGE_OUT_OF_DATE_OUT"
556     ${ARGN}
557     )
558
559  TRIBITS_CHECK_FOR_UNPARSED_ARGUMENTS()
560
561  # Get pass through print level options
562  SET(SHOW_MOST_RECENT_FILES_ARGS)
563  IF (PARSE_SHOW_MOST_RECENT_FILES)
564    APPEND_SET(SHOW_MOST_RECENT_FILES_ARGS SHOW_MOST_RECENT_FILES)
565  ENDIF()
566  IF (PARSE_SHOW_OVERALL_MOST_RECENT_FILES)
567    APPEND_SET(SHOW_MOST_RECENT_FILES_ARGS SHOW_OVERALL_MOST_RECENT_FILES)
568  ENDIF()
569  #PRINT_VAR(SHOW_MOST_RECENT_FILES_ARGS)
570
571  IF (PARSE_SHOW_MOST_RECENT_FILES)
572    SET(PARSE_SHOW_OVERALL_MOST_RECENT_FILES TRUE)
573  ENDIF()
574
575  #
576  # B) Get the list of enabled upstream packages
577  #
578
579  # Only search parent packages to cut down on dirs searched
580  SET(ENABLED_UPSTREAM_PACKAGES)
581  SET(CURRENT_PARENT_PACKAGE)
582  FOREACH(UPSTREAM_SE_PACKAGE ${${PACKAGE_NAME}_FULL_ENABLED_DEP_PACKAGES})
583    # Assume we will append
584    SET(APPEND_PACKAGE ${UPSTREAM_SE_PACKAGE})
585    # If is a subpackage we only append the parent packages
586    SET(PARENT_PACKAGE ${${UPSTREAM_SE_PACKAGE}_PARENT_PACKAGE})
587    IF (PARENT_PACKAGE)
588      SET(APPEND_PACKAGE ${PARENT_PACKAGE})
589    ENDIF()
590    # Append
591    APPEND_SET(ENABLED_UPSTREAM_PACKAGES ${APPEND_PACKAGE})
592  ENDFOREACH()
593  LIST(REMOVE_DUPLICATES ENABLED_UPSTREAM_PACKAGES)
594  #PRINT_VAR(ENABLED_UPSTREAM_PACKAGES)
595
596  #
597  # C) Determine the most recent files on the upstream SE packages
598  #
599
600  IF (PARSE_SHOW_OVERALL_MOST_RECENT_FILES)
601    MESSAGE("\nDetermining most recent source file in upstream SE packages"
602      " from ${PACKAGE_NAME}:")
603  ENDIF()
604  SET(UPSTREAM_SOURCE_BASE_DIRS)
605  FOREACH(UPSTREAM_PACKAGE ${ENABLED_UPSTREAM_PACKAGES})
606    APPEND_SET(UPSTREAM_SOURCE_BASE_DIRS "${${UPSTREAM_PACKAGE}_SOURCE_DIR}")
607  ENDFOREACH()
608  TRIBITS_FIND_MOST_RECENT_SOURCE_FILE_TIMESTAMP(
609    SOURCE_BASE_DIRS ${UPSTREAM_SOURCE_BASE_DIRS}
610    SOURCE_BASE_BASE_DIR "${PROJECT_SOURCE_DIR}"
611    ${SHOW_MOST_RECENT_FILES_ARGS}
612    MOST_RECENT_TIMESTAMP_OUT  MOST_RECENT_UPSTREAM_SOURCE_TIMESTAMP
613    MOST_RECENT_RELATIVE_FILEPATH_OUT MOST_RECENT_UPSTREAM_SOURCE_FILEPATH
614    )
615  #PRINT_VAR(MOST_RECENT_UPSTREAM_SOURCE_FILEPATH)
616
617  IF (PARSE_SHOW_OVERALL_MOST_RECENT_FILES)
618    MESSAGE("\nDetermining most recent binary file in upstream SE packages"
619      " from ${PACKAGE_NAME}:")
620  ENDIF()
621  SET(UPSTREAM_BINARY_BASE_DIRS)
622  FOREACH(UPSTREAM_PACKAGE ${ENABLED_UPSTREAM_PACKAGES})
623    APPEND_SET(UPSTREAM_BINARY_BASE_DIRS "${${UPSTREAM_PACKAGE}_BINARY_DIR}")
624  ENDFOREACH()
625  TRIBITS_FIND_MOST_RECENT_BINARY_FILE_TIMESTAMP(
626    BINARY_BASE_DIRS ${UPSTREAM_BINARY_BASE_DIRS}
627    BINARY_BASE_BASE_DIR "${PROJECT_BINARY_DIR}"
628    ${SHOW_MOST_RECENT_FILES_ARGS}
629    MOST_RECENT_TIMESTAMP_OUT  MOST_RECENT_UPSTREAM_BINARY_TIMESTAMP
630    MOST_RECENT_RELATIVE_FILEPATH_OUT MOST_RECENT_UPSTREAM_BINARY_FILEPATH
631    )
632  #PRINT_VAR(MOST_RECENT_UPSTREAM_BINARY_FILEPATH)
633
634  #
635  # D) Determine the most recent files for the current package
636  #
637
638  IF (PARSE_SHOW_OVERALL_MOST_RECENT_FILES)
639    MESSAGE("\nDetermining most recent source file for current"
640      " package ${PACKAGE_NAME}:")
641  ENDIF()
642  TRIBITS_FIND_MOST_RECENT_SOURCE_FILE_TIMESTAMP(
643    SOURCE_BASE_DIRS ${${PACKAGE_NAME}_SOURCE_DIR}
644    SOURCE_BASE_BASE_DIR "${PROJECT_SOURCE_DIR}"
645    ${SHOW_MOST_RECENT_FILES_ARGS}
646    MOST_RECENT_TIMESTAMP_OUT  MOST_RECENT_THIS_PACKAGE_SOURCE_TIMESTAMP
647    MOST_RECENT_RELATIVE_FILEPATH_OUT MOST_RECENT_THIS_SOURCE_FILEPATH
648    )
649
650  IF (PARSE_SHOW_OVERALL_MOST_RECENT_FILES)
651    MESSAGE("\nDetermining most recent binary file for current"
652      " package ${PACKAGE_NAME}:")
653  ENDIF()
654  TRIBITS_FIND_MOST_RECENT_BINARY_FILE_TIMESTAMP(
655    BINARY_BASE_DIRS  ${${PACKAGE_NAME}_BINARY_DIR}
656    BINARY_BASE_BASE_DIR "${PROJECT_BINARY_DIR}"
657    ${SHOW_MOST_RECENT_FILES_ARGS}
658    MOST_RECENT_TIMESTAMP_OUT  MOST_RECENT_THIS_PACKAGE_BINARY_TIMESTAMP
659    MOST_RECENT_RELATIVE_FILEPATH_OUT  MOST_RECENT_THIS_BINARY_FILEPATH
660    )
661
662  #
663  # E) Compare most recent file time stamps to determine if a rebuild is needed
664  #
665
666  SET(CURRENT_PACKAGE_OUT_OF_DATE_OUT FALSE)
667
668  MESSAGE("\nComparing timestamps of recently updated files:")
669
670  IF (MOST_RECENT_THIS_BINARY_FILEPATH)
671
672    TRIBITS_UPDATE_PACKAGE_OUT_OF_DATE(
673      "upstream SE package source" ${MOST_RECENT_UPSTREAM_SOURCE_TIMESTAMP}
674         "${MOST_RECENT_UPSTREAM_SOURCE_FILEPATH}"
675      ${MOST_RECENT_THIS_PACKAGE_BINARY_TIMESTAMP} "${MOST_RECENT_THIS_BINARY_FILEPATH}"
676      CURRENT_PACKAGE_OUT_OF_DATE_OUT )
677
678    TRIBITS_UPDATE_PACKAGE_OUT_OF_DATE(
679      "upstream SE package binary" ${MOST_RECENT_UPSTREAM_BINARY_TIMESTAMP}
680         "${MOST_RECENT_UPSTREAM_BINARY_FILEPATH}"
681      ${MOST_RECENT_THIS_PACKAGE_BINARY_TIMESTAMP} "${MOST_RECENT_THIS_BINARY_FILEPATH}"
682      CURRENT_PACKAGE_OUT_OF_DATE_OUT )
683
684    TRIBITS_UPDATE_PACKAGE_OUT_OF_DATE(
685      "this package's source" ${MOST_RECENT_THIS_PACKAGE_SOURCE_TIMESTAMP}
686         "${MOST_RECENT_THIS_SOURCE_FILEPATH}"
687      ${MOST_RECENT_THIS_PACKAGE_BINARY_TIMESTAMP} "${MOST_RECENT_THIS_BINARY_FILEPATH}"
688      CURRENT_PACKAGE_OUT_OF_DATE_OUT )
689
690    IF (NOT CURRENT_PACKAGE_OUT_OF_DATE_OUT)
691      MESSAGE("-- This package's most recent binary file"
692        " ${MOST_RECENT_THIS_BINARY_FILEPATH}"
693        " is more recent than its upstream SE package source or binary files"
694        " or this package's source files!")
695    ENDIF()
696
697  ELSE()
698
699    MESSAGE("-- This package has no unfiltered binary files so consider out of date!")
700
701  ENDIF()
702
703  SET(${PARSE_CURRENT_PACKAGE_OUT_OF_DATE_OUT} ${CURRENT_PACKAGE_OUT_OF_DATE_OUT}
704    PARENT_SCOPE)
705
706  IF (${PROJECT_NAME}_ENABLE_CONFIGURE_TIMING)
707    TIMER_GET_RAW_SECONDS(TIMER_STOP_SECONDS)
708    TIMER_PRINT_REL_TIME(${TIMER_START_SECONDS} ${TIMER_STOP_SECONDS}
709      "\nTotal time to check for most recent modified files")
710  ENDIF()
711
712ENDFUNCTION()
713
714
715#
716# Utility functions
717#
718
719
720FUNCTION(TRIBITS_UPDATE_PACKAGE_OUT_OF_DATE
721  DEPENDENCY_TYPE_STRING  DEP_FILE_TIMESTAMP  DEP_FILEPATH
722  THIS_BINARY_FILE_TIMESTAMP  THIS_BINARY_FILEPATH
723  CURRENT_PACKAGE_IS_OUT_OF_DATE_INOUT
724  )
725  IF ("${DEP_FILE_TIMESTAMP}" GREATER "${THIS_BINARY_FILE_TIMESTAMP}")
726    MESSAGE("-- The ${DEPENDENCY_TYPE_STRING} file ${DEP_FILEPATH} is more recent than"
727      " this package's binary file ${THIS_BINARY_FILEPATH}!")
728    SET(${CURRENT_PACKAGE_IS_OUT_OF_DATE_INOUT} TRUE PARENT_SCOPE)
729  ENDIF()
730ENDFUNCTION()
731
732
733FUNCTION(TRIBITS_GET_HUMAN_READABLE_FILE_AND_TIMESTAMP
734  BASE_DIR   CURRENT_FILEPATH
735  HUMAN_READABLE_FILE_AND_TIMESTAMP_OUT
736  )
737  EXECUTE_PROCESS(
738    WORKING_DIRECTORY "${BASE_DIR}"
739    COMMAND ls --full-time "${CURRENT_FILEPATH}"
740    OUTPUT_STRIP_TRAILING_WHITESPACE
741    OUTPUT_VARIABLE  HUMAN_READABLE_FILE_AND_TIMESTAMP
742    )
743  SET(${HUMAN_READABLE_FILE_AND_TIMESTAMP_OUT}
744    ${HUMAN_READABLE_FILE_AND_TIMESTAMP}
745    PARENT_SCOPE)
746ENDFUNCTION()
747
748
749
750
751
752# LocalWords:  ENDFOREACH subpackage subpackages TriBITS timestamp
753