0

The real problem
I want to apply project level source code formatting to all modified files

Current approach
Use add_custom_target in my top-level CMakeLists.txt file to call a script that applies formatting rules to all files the SCM tool reports as modified:

  add_custom_target(Name ALL ${PROJECT_SOURCE_DIR}/../cmake/format_files.bash
         )

This rule is before any add_subdirectory calls, because reformatting should take place before all compilation.

Per the documentation:

ALL

Indicate that this target should be added to the default build target so that it will be run every time (the command cannot be called ALL).

When CMake itself runs (like any modification to the CMakeLists.txt files), all is good.

The Symptom
Suppose I perform some spacing-related modification to file Foo.hh (my rules replace tabs with spaces, for example). My build is likely to include something like this:

Scanning dependencies of target Foo
make[2]: Warning: File `projects/foo/src/Foo.hh' has modification time 8.7 s in the future
...
make[2]: warning:  Clock skew detected.  Your build may be incomplete.

I'm pretty sure it's the source formatting script that somehow runs after dependency scanning (or something like that), modifies Foo.hh, and creates the illusion of clock skew.

What I think the question is
What is the right way to force my build process to assert project standards for source code style prior to building, without potentially creating dependency problems?

Is there a better way to introduce formatting to the build process?

Red Herrings
At first, I thought I was dealing with a true clock skew problem; my development environment is on a VMware VM, and we have had some issues with time in the past, but now I'm 99% sure that all the VMs are using host time. Furthermore, a simple test like this (in the same filesystem as my builds) proves there is no intrinsic clock skew:

$ date ; touch foo ; ls --time-style=+%H:%M:%S -l foo ; date
Thu Jan 17 12:48:59 MST 2019
-rw-rw-r--. 1 1001 1001 0 12:48:59 foo
Thu Jan 17 12:48:59 MST 2019

A key facet of the source code formatting process is that there is no deterministic way to know which files might be modified in the script and which will not. Files that comply with project standards are not touched.

For completeness, here is the script:

#!/bin/bash

# This script is intended to format any modified files to project standards

# Change to the project root
cd $(dirname $0)/..

outfile=format.log

file_list=$( git status --short --untracked-files=all src \
    | awk '/^( M|\?\?) .*\.(cpp|hh)/ {print $2}' )

# If we haven't changed any files, exit gracefully
[[ -z $file_list ]] && exit 0

# Format the current working set
echo >> ${outfile}
date '+%Y-%m-%dT%H:%M:%S.%N: ' >> ${outfile}
astyle --project $file_list >>${outfile} 2>&1

This script appends to an output file (I'll probably remove that at some point) that looks like this:

2019-01-17T18:54:20.641765133:
Unchanged  src/Foo.cpp
Formatted  src/Foo.hh
Unchanged  src/Bar.cpp
jwm
  • 1,504
  • 1
  • 14
  • 29
  • This is a warning from `make`, not really from `cmake`. The `add_custom_target` should work as it is (I would add PRE_BUILD with command). `My build is likely` - does this happen every time? I would suggest to get to the makefile for `foo` target and temporary get `ls` output from the dependency inside `foo` target with date, see if it's really a time skew. `VMware VM` - are the files mounted from a remote location? Are the files copied on build or similar? – KamilCuk Jan 18 '19 at 02:55
  • I realize it is a warning from `make`, but it is caused by the way `cmake` generates the makefiles. I can reproduce it by making any change to `Foo.hh` that will trigger the formatter. – jwm Jan 18 '19 at 03:16
  • I specifically noted that the VMware theory for time skew was a red herring, and demonstrated how I proved it. – jwm Jan 18 '19 at 03:18
  • did you use `add_dependencies` to make the formatter run before compiling the targets it formats? If not, that could be your problem. – starball Sep 06 '22 at 07:02

1 Answers1

0

Based on the discussion at https://discourse.cmake.org/t/cmake-pre-build-command/1083, the answer is "don't do that". Formatting can be a target and building can be a target, but having a build step that modifies the dependencies of another build step (after the dependency tree has been evaluated) is bad.

Instead of formatting my code as part of the build, I added it as a CI check on the build server: if formatting would change the code, the build fails. I also created a pre-commit hook to tell me if my code needs formatting. I don't like hooks that change the code checked in; changed code should always be compiled before commit.

jwm
  • 1,504
  • 1
  • 14
  • 29