cmakecmake-language

How to detect accidental function overrides in CMake?


I just discovered a copy and paste error in my CMake code:

function(name)
   # do something ...
endfunction()

function(name)
   # do something else ...
endfunction()

I had copied, renamed and changed the function several times. Apparently I created one copy too many, so that the original function now existed twice. Later I changed one of the two. The error was discovered because the original version was defined after the changed function and therefore my changes had no effect.

Is there a way to detect a double defined function?


Solution

  • It seems that CMake itself has no switch which prevents double function definition. You could run cmake in a trace mode:

    cmake --trace-format=json-v1 --trace-redirect=trace.json <... other params>
    

    The resulted trace.json file in JSON format will accumulate (amond other info) all calls to 'function' command:

    ...
    {"args":["my_func"],"cmd":"function","file":"/home/tester/tests/cmake/CMakeLists.txt","frame":1,"line":4,"time":1684839183.3986549}
    {"args":["my_func"],"cmd":"function","file":"/home/tester/tests/cmake/CMakeLists.txt","frame":1,"line":8,"time":1684839183.398663}
    ...
    

    Then you may write a script which reads resulted JSON file and finds calls to 'function' command with the same names.

    E.g. following jq script:

    # Select only functions definitions
    map(select(.cmd=="function")) |
    # Simplify information to only a function name and its location.
    map({"func": .args[0], "file": (.file + ":" + (.line|tostring))}) |
    # Group by function names
    group_by(.func) |
    # Select those groups which have more than one entry.
    map(select(length>1))
    

    with usage

    jq -s -f <path/to/script> trace.json
    

    could produce such output:

    [
      [
        {
          "func": "my_func",
          "file": "/home/tester/tests/cmake/CMakeLists.txt:4"
        },
        {
          "func": "my_func",
          "file": "/home/tester/tests/cmake/CMakeLists.txt:8"
        }
      ]
    ]
    

    Note, that the approach above won't work in case CMake will find a syntax error (like with if(AAA AAA) command) while processing your project: in that case the trace file will be empty.

    However, hitting e.g. message(FATAL_ERROR) won't prevent CMake to trace all commands before the failure.