Search code examples
cmakecmake-language

Start reading file from a specific line using CMAKE


I want to start reading a file from a specific line. Cmake official docs suggest using file() with offset but I am not sure about its usage. The file that I want to read is test.mak:

# -----------------------------------------------------------------------------
## TEST
# -----------------------------------------------------------------------------
TEST_COMPONENTS      ?= ABC DEF GHI

# SYMBOLS
SYMBOLS_PROJ ?= A002
SYMBOLS_LABEL   ?= TEST_A002_FINAL

I have a cmake file (the function is from internet and it works with my use case) where i want to read the test.mak file starting from "#SYMBOLS" so that the macros defined before this line are ignored/skipped, and then i want to set the macros in my current cmake:

function(Fileread MKFile)
  file(READ "${MKFile}" FileContents [OFFSET "# SYMBOLS"])
  string(REPLACE "?" "" FileContents ${FileContents})
  string(REPLACE "\\\n" "" FileContents ${FileContents})
  string(REPLACE "\n" ";" FileLines ${FileContents})
  list(REMOVE_ITEM FileLines "")
  foreach(line ${FileLines})
    string(REPLACE "=" ";" line_split ${line})
    list(LENGTH line_split count)
    if (count LESS 2)
      message(STATUS "Skipping ${line}")
      continue()
    endif()
    list(GET line_split -1 value)
    string(STRIP "${value}" value)
    separate_arguments(value)
    list(REMOVE_AT line_split -1)
    foreach(var_name ${line_split})
      string(STRIP ${var_name} var_name)
      set(${var_name} ${value} PARENT_SCOPE)
    endforeach()
  endforeach()
endfunction()

Fileread("test.mak")

The offset setting is not working as a result of which i am also getting the macro TEST_COMPONENTS which i don't need. NOTE: TEST_COMPONENTS is just an example, there are multiple lines of macro definitions before "# SYMBOLS" that i would like to skip. Thanks for any suggestions to solve this in advance.


Solution

  • Use file(STRINGS) to read the lines of the text file into a list variable. You could then use list(POP_FRONT) until you encounter a matching line.

    # Line 1
    # Line 2
    # Line 3
    # Line 4
    
    file(STRINGS ${CMAKE_CURRENT_LIST_FILE} THIS_FILE)
    
    set(REDUCTION_SUCCESS False)
    
    while(THIS_FILE)
        list(POP_FRONT THIS_FILE LINE)
        if (LINE MATCHES "^# SYMBOLS.*")
            set(REDUCED_FILE ${LINE} ${THIS_FILE})
            set(REDUCTION_SUCCESS True)
            break()
        endif()
    endwhile()
    
    if (REDUCTION_SUCCESS)
        # use the contents of the REDUCED_FILE variable
        # (print all remaining lines for the purpose of demonstation)
        foreach(_LINE IN LISTS REDUCED_FILE)
            message(STATUS "${_LINE}")
        endforeach()
    else()
        message(FATAL_ERROR "No line containing '# SYMBOLS' found")
    endif()
    

    Replace one of the # Line 1 comments with # SYMBOLS to get a successful outcome. For simplicity this is just a cmake script that can be run with cmake -P script.cmake. The script file parses itself.


    If the number of lines to skip is known, you could simplify the logic after the file(STRINGS) command to a single list(SUBLIST) call.