在没有包装脚本的情况下将cmake从源代码中构建出来?

时间:2022-12-14 12:45:08

I'm trying to get cmake to build into a directory 'build', as in project/build, where the CMakeLists.txt is in project/.

我正在尝试将cmake构建成一个目录'build',就像在项目/构建中,CMakeLists一样。三是在项目/。

I know I can do:

我知道我能做到:

mkdir build
cd build
cmake ../

but that is cumbersome. I could put it in a script and call it, but then it's unpleasant to provide different arguments to cmake (like -G "MSYS Makefiles"), or I would need to edit this file on each platform.

但是这很麻烦。我可以把它放在一个脚本中,然后调用它,但是,提供不同的参数给cmake(比如-G的“MSYS makefile”)是不愉快的,或者我需要在每个平台上编辑这个文件。

Preferably I would do something like SET(CMAKE_OUTPUT_DIR build) in the main CMakeLists.txt. Please tell me that this is possible, and if so, how? Or some other out of source build method that makes it easy to specify different arguments?

最好在主CMakeLists.txt中做一些类似SET(CMAKE_OUTPUT_DIR构建)的操作。请告诉我这是可能的,如果是,如何?或者是其他的源代码构建方法,使得指定不同的参数变得容易?

3 个解决方案

#1


30  

You can use the undocumented CMake options -H and -B to specify the source and binary directory upon invoking cmake:

您可以使用无记录的CMake选项-H和-B在调用CMake时指定源和二进制目录:

cmake -H. -Bbuild -G "MSYS Makefiles"

This will look for the CMakeLists.txt in the current folder and create a build folder (if it does not yet exist) in it.

这将寻找CMakeLists。在当前文件夹中创建一个构建文件夹(如果它还不存在的话)。

#2


5  

A solution that I found recently is to combine the out-of-source build concept with a Makefile wrapper.

我最近发现的一个解决方案是将out- source构建概念与Makefile包装器结合起来。

In my top-level CMakeLists.txt file, I include the following to prevent in-source builds:

在我的*CMakeLists。在txt文件中,我包括以下内容,以防止内部构建:

if ( ${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR} )
    message( FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there. You may need to remove CMakeCache.txt." )
endif()

Then, I create a top-level Makefile, and include the following:

然后,我创建一个*Makefile,并包含以下内容:

# -----------------------------------------------------------------------------
# CMake project wrapper Makefile ----------------------------------------------
# -----------------------------------------------------------------------------

SHELL := /bin/bash
RM    := rm -rf
MKDIR := mkdir -p

all: ./build/Makefile
    @ $(MAKE) -C build

./build/Makefile:
    @  ($(MKDIR) build > /dev/null)
    @  (cd build > /dev/null 2>&1 && cmake ..)

distclean:
    @  ($(MKDIR) build > /dev/null)
    @  (cd build > /dev/null 2>&1 && cmake .. > /dev/null 2>&1)
    @- $(MAKE) --silent -C build clean || true
    @- $(RM) ./build/Makefile
    @- $(RM) ./build/src
    @- $(RM) ./build/test
    @- $(RM) ./build/CMake*
    @- $(RM) ./build/cmake.*
    @- $(RM) ./build/*.cmake
    @- $(RM) ./build/*.txt

ifeq ($(findstring distclean,$(MAKECMDGOALS)),)
    $(MAKECMDGOALS): ./build/Makefile
    @ $(MAKE) -C build $(MAKECMDGOALS)
endif

The default target all is called by typing make, and invokes the target ./build/Makefile.

所有的默认目标都是通过键入make来调用的,并调用目标。/build/Makefile。

The first thing the target ./build/Makefile does is to create the build directory using $(MKDIR), which is a variable for mkdir -p. The directory build is where we will perform our out-of-source build. We provide the argument -p to ensure that mkdir does not scream at us for trying to create a directory that may already exist.

/build/Makefile所做的第一件事就是使用$(MKDIR)创建构建目录,它是MKDIR -p的一个变量。我们将在目录构建中执行我们的源代码构建。我们提供了参数-p,以确保mkdir不会对我们试图创建可能已经存在的目录发出尖叫。

The second thing the target ./build/Makefile does is to change directories to the build directory and invoke cmake.

/build/Makefile所做的第二件事是将目录更改为构建目录并调用cmake。

Back to the all target, we invoke $(MAKE) -C build, where $(MAKE) is a Makefile variable automatically generated for make. make -C changes the directory before doing anything. Therefore, using $(MAKE) -C build is equivalent to doing cd build; make.

回到所有目标,我们调用$(MAKE) -C构建,其中$(MAKE)是自动生成的Makefile变量。make -C在做任何事情之前更改目录。因此,使用$(MAKE) -C构建就相当于进行cd构建;制作。

To summarize, calling this Makefile wrapper with make all or make is equivalent to doing:

总而言之,调用这个Makefile包装器使所有或make都等于做:

mkdir build
cd build
cmake ..
make 

The target distclean invokes cmake .., then make -C build clean, and finally, removes all contents from the build directory. I believe this is exactly what you requested in your question.

目标distclean调用cmake ..然后,让-C构建干净,最后,从构建目录中删除所有内容。我相信这正是你所要求的。

The last piece of the Makefile evaluates if the user-provided target is or is not distclean. If not, it will change directories to build before invoking it. This is very powerful because the user can type, for example, make clean, and the Makefile will transform that into an equivalent of cd build; make clean.

Makefile的最后一个部分评估用户提供的目标是否为distclean。如果不是,它将在调用之前更改构建的目录。这是非常强大的,因为用户可以键入,例如,进行清理,而Makefile将转换为等效的cd构建;做清洁。

In conclusion, this Makefile wrapper, in combination with a mandatory out-of-source build CMake configuration, make it so that the user never has to interact with the command cmake. This solution also provides an elegant method to remove all CMake output files from the build directory.

总之,这个Makefile包装器与一个强制的非源构建CMake配置相结合,使得用户不必与命令CMake交互。此解决方案还提供了一种优雅的方法,可以从构建目录中删除所有CMake输出文件。

P.S. In the Makefile, we use the prefix @ to suppress the output from a shell command, and the prefix @- to ignore errors from a shell command. When using rm as part of the distclean target, the command will return an error if the files do not exist (they may have been deleted already using the command line with rm -rf build, or they were never generated in the first place). This return error will force our Makefile to exit. We use the prefix @- to prevent that. It is acceptable if a file was removed already; we want our Makefile to keep going and remove the rest.

在Makefile中,我们使用前缀@来抑制shell命令的输出,并使用前缀@来忽略shell命令中的错误。当使用rm作为distclean目标的一部分时,如果文件不存在,该命令将返回一个错误(他们可能已经使用rm -rf构建的命令行已经删除了,或者从一开始就没有生成)。这个返回错误将强制我们的Makefile退出。我们使用前缀@来阻止它。如果一个文件已经被删除,这是可以接受的;我们希望Makefile继续执行并删除其余部分。

Another thing to note: This Makefile may not work if you use a variable number of CMake variables to build your project, for example, cmake .. -DSOMEBUILDSUSETHIS:STRING="foo" -DSOMEOTHERBUILDSUSETHISTOO:STRING="bar". This Makefile assumes you invoke CMake in a consistent way, either by typing cmake .. or by providing cmake a consistent number of arguments (that you can include in your Makefile).

另外要注意的一点是:如果您使用可变数量的CMake变量来构建项目,那么这个Makefile可能不起作用,例如,CMake ..-DSOMEBUILDSUSETHIS:字符串= " foo " -DSOMEOTHERBUILDSUSETHISTOO:字符串=“酒吧”。这个Makefile假设您以一致的方式调用CMake,或者通过输入CMake ..或者通过提供cmake一致的参数数量(可以在Makefile中包含)。

Finally, credit where credit is due. This Makefile wrapper was adapted from the Makefile provided by the C++ Application Project Template.

最后,信用在哪里到期。这个Makefile包装器是根据c++应用程序项目模板提供的Makefile改编的。

This answer was originally posted here. I thought it applied to your situation as well.

这个答案最初发表在这里。我认为它也适用于你的情况。

#3


1  

Based on the previous answers, I wrote the following module that you can include to enforce an out-of-source build.

根据前面的答案,我编写了下面的模块,您可以包括执行一个源代码构建。

set(DEFAULT_OUT_OF_SOURCE_FOLDER "cmake_output")

if (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
    message(WARNING "In-source builds not allowed. CMake will now be run with arguments:
        cmake -H. -B${DEFAULT_OUT_OF_SOURCE_FOLDER}
")

    # Run CMake with out of source flag
    execute_process(
            COMMAND ${CMAKE_COMMAND} -H. -B${DEFAULT_OUT_OF_SOURCE_FOLDER}
            WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})

    # Cause fatal error to stop the script from further execution
    message(FATAL_ERROR "CMake has been ran to create an out of source build.
This error prevents CMake from running an in-source build.")
endif ()

This works, however I already noticed two downsides:

不过,我已经注意到两个缺点:

  • When the user is lazy and simply runs cmake ., they will always see a FATAL_ERROR. I could not find another way to prevent CMake from doing any other operations and exit early.
  • 当用户偷懒并简单地运行cmake时,他们总是会看到FATAL_ERROR。我找不到另一种方法来阻止CMake做任何其他的操作并且提前退出。
  • Any command line arguments passed to the original call to cmake will not be passed to the "out-of-source build call".
  • 传递给cmake的原始调用的任何命令行参数都不会传递给“非源构建调用”。

Suggestions to improve this module are welcome.

欢迎提出改进此模块的建议。

#1


30  

You can use the undocumented CMake options -H and -B to specify the source and binary directory upon invoking cmake:

您可以使用无记录的CMake选项-H和-B在调用CMake时指定源和二进制目录:

cmake -H. -Bbuild -G "MSYS Makefiles"

This will look for the CMakeLists.txt in the current folder and create a build folder (if it does not yet exist) in it.

这将寻找CMakeLists。在当前文件夹中创建一个构建文件夹(如果它还不存在的话)。

#2


5  

A solution that I found recently is to combine the out-of-source build concept with a Makefile wrapper.

我最近发现的一个解决方案是将out- source构建概念与Makefile包装器结合起来。

In my top-level CMakeLists.txt file, I include the following to prevent in-source builds:

在我的*CMakeLists。在txt文件中,我包括以下内容,以防止内部构建:

if ( ${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR} )
    message( FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there. You may need to remove CMakeCache.txt." )
endif()

Then, I create a top-level Makefile, and include the following:

然后,我创建一个*Makefile,并包含以下内容:

# -----------------------------------------------------------------------------
# CMake project wrapper Makefile ----------------------------------------------
# -----------------------------------------------------------------------------

SHELL := /bin/bash
RM    := rm -rf
MKDIR := mkdir -p

all: ./build/Makefile
    @ $(MAKE) -C build

./build/Makefile:
    @  ($(MKDIR) build > /dev/null)
    @  (cd build > /dev/null 2>&1 && cmake ..)

distclean:
    @  ($(MKDIR) build > /dev/null)
    @  (cd build > /dev/null 2>&1 && cmake .. > /dev/null 2>&1)
    @- $(MAKE) --silent -C build clean || true
    @- $(RM) ./build/Makefile
    @- $(RM) ./build/src
    @- $(RM) ./build/test
    @- $(RM) ./build/CMake*
    @- $(RM) ./build/cmake.*
    @- $(RM) ./build/*.cmake
    @- $(RM) ./build/*.txt

ifeq ($(findstring distclean,$(MAKECMDGOALS)),)
    $(MAKECMDGOALS): ./build/Makefile
    @ $(MAKE) -C build $(MAKECMDGOALS)
endif

The default target all is called by typing make, and invokes the target ./build/Makefile.

所有的默认目标都是通过键入make来调用的,并调用目标。/build/Makefile。

The first thing the target ./build/Makefile does is to create the build directory using $(MKDIR), which is a variable for mkdir -p. The directory build is where we will perform our out-of-source build. We provide the argument -p to ensure that mkdir does not scream at us for trying to create a directory that may already exist.

/build/Makefile所做的第一件事就是使用$(MKDIR)创建构建目录,它是MKDIR -p的一个变量。我们将在目录构建中执行我们的源代码构建。我们提供了参数-p,以确保mkdir不会对我们试图创建可能已经存在的目录发出尖叫。

The second thing the target ./build/Makefile does is to change directories to the build directory and invoke cmake.

/build/Makefile所做的第二件事是将目录更改为构建目录并调用cmake。

Back to the all target, we invoke $(MAKE) -C build, where $(MAKE) is a Makefile variable automatically generated for make. make -C changes the directory before doing anything. Therefore, using $(MAKE) -C build is equivalent to doing cd build; make.

回到所有目标,我们调用$(MAKE) -C构建,其中$(MAKE)是自动生成的Makefile变量。make -C在做任何事情之前更改目录。因此,使用$(MAKE) -C构建就相当于进行cd构建;制作。

To summarize, calling this Makefile wrapper with make all or make is equivalent to doing:

总而言之,调用这个Makefile包装器使所有或make都等于做:

mkdir build
cd build
cmake ..
make 

The target distclean invokes cmake .., then make -C build clean, and finally, removes all contents from the build directory. I believe this is exactly what you requested in your question.

目标distclean调用cmake ..然后,让-C构建干净,最后,从构建目录中删除所有内容。我相信这正是你所要求的。

The last piece of the Makefile evaluates if the user-provided target is or is not distclean. If not, it will change directories to build before invoking it. This is very powerful because the user can type, for example, make clean, and the Makefile will transform that into an equivalent of cd build; make clean.

Makefile的最后一个部分评估用户提供的目标是否为distclean。如果不是,它将在调用之前更改构建的目录。这是非常强大的,因为用户可以键入,例如,进行清理,而Makefile将转换为等效的cd构建;做清洁。

In conclusion, this Makefile wrapper, in combination with a mandatory out-of-source build CMake configuration, make it so that the user never has to interact with the command cmake. This solution also provides an elegant method to remove all CMake output files from the build directory.

总之,这个Makefile包装器与一个强制的非源构建CMake配置相结合,使得用户不必与命令CMake交互。此解决方案还提供了一种优雅的方法,可以从构建目录中删除所有CMake输出文件。

P.S. In the Makefile, we use the prefix @ to suppress the output from a shell command, and the prefix @- to ignore errors from a shell command. When using rm as part of the distclean target, the command will return an error if the files do not exist (they may have been deleted already using the command line with rm -rf build, or they were never generated in the first place). This return error will force our Makefile to exit. We use the prefix @- to prevent that. It is acceptable if a file was removed already; we want our Makefile to keep going and remove the rest.

在Makefile中,我们使用前缀@来抑制shell命令的输出,并使用前缀@来忽略shell命令中的错误。当使用rm作为distclean目标的一部分时,如果文件不存在,该命令将返回一个错误(他们可能已经使用rm -rf构建的命令行已经删除了,或者从一开始就没有生成)。这个返回错误将强制我们的Makefile退出。我们使用前缀@来阻止它。如果一个文件已经被删除,这是可以接受的;我们希望Makefile继续执行并删除其余部分。

Another thing to note: This Makefile may not work if you use a variable number of CMake variables to build your project, for example, cmake .. -DSOMEBUILDSUSETHIS:STRING="foo" -DSOMEOTHERBUILDSUSETHISTOO:STRING="bar". This Makefile assumes you invoke CMake in a consistent way, either by typing cmake .. or by providing cmake a consistent number of arguments (that you can include in your Makefile).

另外要注意的一点是:如果您使用可变数量的CMake变量来构建项目,那么这个Makefile可能不起作用,例如,CMake ..-DSOMEBUILDSUSETHIS:字符串= " foo " -DSOMEOTHERBUILDSUSETHISTOO:字符串=“酒吧”。这个Makefile假设您以一致的方式调用CMake,或者通过输入CMake ..或者通过提供cmake一致的参数数量(可以在Makefile中包含)。

Finally, credit where credit is due. This Makefile wrapper was adapted from the Makefile provided by the C++ Application Project Template.

最后,信用在哪里到期。这个Makefile包装器是根据c++应用程序项目模板提供的Makefile改编的。

This answer was originally posted here. I thought it applied to your situation as well.

这个答案最初发表在这里。我认为它也适用于你的情况。

#3


1  

Based on the previous answers, I wrote the following module that you can include to enforce an out-of-source build.

根据前面的答案,我编写了下面的模块,您可以包括执行一个源代码构建。

set(DEFAULT_OUT_OF_SOURCE_FOLDER "cmake_output")

if (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
    message(WARNING "In-source builds not allowed. CMake will now be run with arguments:
        cmake -H. -B${DEFAULT_OUT_OF_SOURCE_FOLDER}
")

    # Run CMake with out of source flag
    execute_process(
            COMMAND ${CMAKE_COMMAND} -H. -B${DEFAULT_OUT_OF_SOURCE_FOLDER}
            WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})

    # Cause fatal error to stop the script from further execution
    message(FATAL_ERROR "CMake has been ran to create an out of source build.
This error prevents CMake from running an in-source build.")
endif ()

This works, however I already noticed two downsides:

不过,我已经注意到两个缺点:

  • When the user is lazy and simply runs cmake ., they will always see a FATAL_ERROR. I could not find another way to prevent CMake from doing any other operations and exit early.
  • 当用户偷懒并简单地运行cmake时,他们总是会看到FATAL_ERROR。我找不到另一种方法来阻止CMake做任何其他的操作并且提前退出。
  • Any command line arguments passed to the original call to cmake will not be passed to the "out-of-source build call".
  • 传递给cmake的原始调用的任何命令行参数都不会传递给“非源构建调用”。

Suggestions to improve this module are welcome.

欢迎提出改进此模块的建议。