2

I am rewriting a cmake build script from an existing GNU makefile. It is the first cmake scrip I have tried to write.

GNU make provide vpaths command, but i can not find a corrsponding command.

I have implemented a macro to resolve it, but i am finding it too hard to cover all exception cases.

Do you have any solution for it?


It takes some time under many source and dir.

macro( vpath resultSource NumOfSource NumOfDir sourceList DirList ) math(EXPR startDirIndex "3 + ${NumOfSource}" ) math(EXPR endIndex "2 + ${NumOfSource} + ${NumOfDir}" )

message(STATUS "startDirIndex: ${startDirIndex}")
message(STATUS "endIndex: ${endIndex}")

set( __SRC_LIST__ )
set( __SRC_DIR__ )

set( pos 0 )
foreach(item ${ARGV})
    if( ${pos} EQUAL ${startDirIndex} 
        OR ${pos} GREATER ${startDirIndex} )

        list( APPEND __SRC_DIR__ ${item} )
        #message(STATUS "DIR:${item}")

    elseif( ${pos} GREATER 2 )

        list( APPEND __SRC_LIST__ ${item} )
        #message(STATUS "SRC:${item}")

    endif( ${pos} EQUAL ${startDirIndex} 
        OR ${pos} GREATER ${startDirIndex} )

    math(EXPR pos "${pos}+1")
endforeach(item ${ARGV})

#message(STATUS "__SRC_LIST__: ${__SRC_LIST__}")
#message(STATUS "__SRC_DIR__: ${__SRC_DIR__}")

#set( SOURCE_EXTENTION cpp c C CPP)
set( SOURCE_EXTENTION cpp )
#list( APPEND SOURCE_EXTENTION ${CMAKE_CXX_SOURCE_FILE_EXTENSIONS} ${CMAKE_C_SOURCE_FILE_EXTENSIONS} )

#message(STATUS "SOURCE_EXTENTION: ${SOURCE_EXTENTION}")

set( temp_list "" )

foreach( dir ${__SRC_DIR__} )
    foreach( src ${__SRC_LIST__} )
        set( cand_src )
        foreach( postfix ${SOURCE_EXTENTION})
            set( cand_src "${dir}/${src}.${postfix}" )
            file(GLOB result ${cand_src} )
            if( NOT ${result} STREQUAL "" )
                list( APPEND temp_list ${result} )                    
            endif( NOT ${result} STREQUAL "" )                            
        endforeach( postfix ${SOURCE_EXTENTION})
    endforeach( src ${__SRC_LIST__} )
endforeach( dir ${__SRC_DIR__} )

set( ${resultSource} ${temp_list} )

#message(STATUS "temp_list: ${temp_list}")

endmacro( vpath resultSource NumOfSource NumOfDir sourceList DirList )

casperOne
  • 73,706
  • 19
  • 184
  • 253
jongwon.kwak
  • 143
  • 1
  • 7

1 Answers1

4

This is what I think. Full disclaimer: I'm not really a cmake guru, and this answer is solely based on my research after reading the question. I don't have a project to test this on, and I have never used vpath in a Makefile that I've written.

In addition, this question does not post an example Makefile, nor does it post the macro used by the OP, which makes me hesitant about giving a "use this" answer, but here goes.

Firstly: why are we using vpath in our Makefile? Because I didn't know what vpath was, I looked it up. From the GNU Make Manual, section 4.5:

The directory search features of make facilitate this by searching several directories automatically to find a prerequisite... Similar to the VPATH variable, but more selective, is the vpath directive (note lower case), which allows you to specify a search path for a particular class of file names: those that match a particular pattern. Thus you can supply certain search directories for one class of file names and other directories (or none) for other file names.

So, you're using vpath to find files that are spread over several directories. These files, I'm further presuming, are either source files or are intermediate files created during the build process.

I found several examples of vpath, which seem indicative of it's usage - this StackOverflow question, this one and finally, Managing Projects with GNU make, 3rd Edition section 2.3.

So that's why you'd be using vpath. So, moving on:

Secondly, how do you achieve the same thing using cmake. And this is where further input from the OP would be helpful.

If you have multiple targets that you are building from a directory structure like this:

|- library1/
|-- {C++ Source}
|- Tool/
|-- {C++ Source}
|- Project/
|-- src/
|---- {C++ Source}

You should be using multiple CMakeLists.txt, one in each of the subdirectories. In library1, you'd build the library (ADD_LIBRARY( libNAME src1.cxx src2.cxx src3.cxx )), in Tool you'd add an executable (ADD_EXECUTABLE( theTOOL src1.cxx src2.cxx )). Finally, in Project/src/ you'd build your project executable with the ADD_EXECUTABLE, linking it to libNAME that you've built in library1.

In this scenario, you never need to search, because your source is explicitly placed in the CMakeLists.txt.

If you are not doing something like the above, and your source is just spread over many many directories, something like this:

|-Project/
|--src/
|----maths/
|------ { SOURCE FILES }
|----sound/
|------ { SOURCE FILES }
|----imagegeneration/
|------ { SOURCE FILES }

In this case, you're only building the one thing, and you're just spreading your source out for ease-of-use. In this case, there are several options for you.

  1. You can use the answer posted here to automatically add all files of a type to a target. This is NOT supposed to be done! From the cmake --help-command file (emphasis mine):

    GLOB will generate a list of all files that match the globbing expressions and store it into the variable ... We do not recommend using GLOB to collect a list of source files from your source tree.
    [See below for the reason why]
  2. You can use the add_subdirectory directive to add multiple subdirectories, one for each of your source subdirectory. This is also not ideal. (Some supporting documentation: a link which discusses the use of add_subdirectory in this manner)

  3. You can use the aux_source_directory to collect the source files from the specified directory and store in a variable. (Read cmake --help-command aux_source_directory if you're interested in it). However, this, too, is not recommended.

So why shouldn't you use (1), (2) or (3)? cmake is not a replacement for make or Makefiles. Rather, cmake generates a build system, which you then use to build your project. This is a distinction worth noting. While you could easily add multiple sources to your CMakeLists.txt automatically, you will cause an ambiguity for cmake: cmake will be unable create a build system that is aware of source files to determine what has changed!

This is the issue with all three methods mentioned above. From cmake --help-command aux_source_directory:

While this seems to work, there is no way for CMake to generate a build system that knows when a new source file has been added. Normally the generated build system knows when it needs to rerun CMake because the CMakeLists.txt file is modified to add a new source. When the source is just added to the directory without modifying this file, one would have to manually rerun CMake to generate a build system incorporating the new file.

Summation: how should you add source files to cmake?

When using cmake, you should be explicitly adding source files to each of your CMakeLists.txt. If you are using multiple directories to separate logical pieces of code, that should be explicitly set in a CMakeLists.txt, something similar to the following:

set ( MyApplication_SRC_FILES
    maths/a.cpp
    maths/b.cpp
    sound/c.cpp
    sound/d.cpp
    imagegeneration/e.cpp
    imagegeneration/f.cpp
)
ADD_EXECUTABLE ( MyApplication ${MyApplication_SRC_FILES} )

This then allows cmake to generate a build system which can build your code. There are appropriate methods to add numerous files in directories to the build system, outlined in (1), (2) and (3) above; but they should be treated with caution and not used just because "that's how I did it with my Makefile". You don't write the build system itself; this is a step back from the Makefile, and as such should be treated slightly differently.

Community
  • 1
  • 1
simont
  • 68,704
  • 18
  • 117
  • 136
  • Thank you. legacy makefile have too many source, So I can not rewrite your suggest option- 1,2,3 - I write some macro. it have some strange. i write below. – jongwon.kwak Mar 01 '12 at 04:26