首页 > 解决方案 > Cmake:如何构建自定义编译器二进制文件,然后将其用于某些目标?

问题描述

我需要构建一个自定义 C++ 编译器二进制文件,然后将其用于项目中的某些目标。

在现代 CMake 中最好的方法是什么?

我从编译器目标设置了一个依赖项,它按预期工作,但是然后呢,使用“生成器表达式”来获取编译器目标二进制名称?

set(CMAKE_CXX_COMPILER ...)-- 要么它不理解生成器表达式,要么我以某种方式滥用它。

有没有办法只为特定目标设置编译器?据我了解,set(CMAKE_CXX_COMPILER)适用于当前目录及其子目录。

标签: c++cmake

解决方案


我用非 C++ 源代码 ( )为示例应用程序 ( ) 创建了一个MCVE ,它必须通过中间 C++ 源进行编译。App/app.ccApp/text.txt

示例目录树:

└┬─ ./
 ├─── CMakeLists.txt
 ├─┬─ App/
 │ ├─── CMakeLists.txt
 │ ├─── app.cc
 │ └─── text.txt
 └─┬─ Tool/
   ├─── CMakeLists.txt
   └─── tool.cc

该文件./CMakeLists.txt是主项目文件(为VisualStudio提供解决方案):

project(App)

cmake_minimum_required(VERSION 3.10.0)

set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

add_subdirectory(App)
add_subdirectory(Tool)

./Tool/tool.cc用于从文本文件生成 C++ 源代码和标头的构建工具的源代码:

// a sample tool converting a text file to a c++ source

#include <fstream>
#include <iostream>
#include <string>

int main(int argc, char **argv)
{
  if (argc < 3) {
    std::cerr <<
      "ERROR in tool: Missing arguments!\n"
      "\n"
      "Usage:\n"
      "tool TXT_FILE CC_FILE\n";
    return -1;
  }
  std::ifstream fIn(argv[1]);
  if (!fIn.good()) {
    std::cerr << "ERROR: Cannot open '" << argv[1] << "' for reading!\n";
    return -1;
  }
  const std::string fileH = std::string(argv[2]) + ".h";
  std::ofstream fOutH(fileH);
  if (!fOutH.good()) {
    std::cerr << "ERROR: Cannot open '" << fileH << "' for writing!\n";
    return -1;
  }
  const std::string fileCC = std::string(argv[2]) + ".cc";
  std::ofstream fOutCC(fileCC);
  if (!fOutCC.good()) {
    std::cerr << "ERROR: Cannot open '" << fileCC << "' for writing!\n";
    return -1;
  }
  fOutCC << "#include \"" << fileH << "\"\n\n";
  for (std::string buffer; std::getline(fIn, buffer);) {
    const size_t i = buffer.find('=');
    if (i < buffer.size()) {
      fOutH << "extern const char *const " << buffer.substr(0, i) << ";\n";
      fOutCC << "const char *const " << buffer.substr(0, i)
        << " = \"" << buffer.substr(i + 1) << "\";\n";
    }
  }
  fOutH.close();
  if (!fOutH.good()) {
    std::cerr << "ERROR: Couldn't complete writing of '" << fileH << "'!\n";
    return -1;
  }
  fOutCC.close();
  if (!fOutCC.good()) {
    std::cerr << "ERROR: Couldn't complete writing of '" << fileCC << "'!\n";
    return -1;
  }
  return 0;
}

./Tool/CMakeLists.txt构建工具的文件:

project(Tool)

add_executable(tool
  tool.cc)

set_property(TARGET tool
  PROPERTY FOLDER "Tools")

该文件./App/text.txt- 一个文本文件,必须转换为生成的源文件text.cctext.h

text1=Hello World.
text2=Text built with tool -> VC++

./App/app.cc包含的来源text.h

// a sample app using an indirect built source file

#include <iostream>

#include "text.h"

int main()
{
  std::cout << "text1: '" << text1 << "'\n";
  std::cout << "text2: '" << text2 << "'\n";
  return 0;
}

最后,./App/CMakeLists.txt介绍自定义构建步骤:

# custom build step
add_custom_command(
  OUTPUT
    text.cc text.h
  COMMAND
    tool "${CMAKE_CURRENT_SOURCE_DIR}/text.txt" text
  MAIN_DEPENDENCY
    text.txt
  DEPENDS
    tool
  COMMENT
    "generate C++ sources from text"
  VERBATIM)

# regular C++ build
add_executable(app
  app.cc          # regular source files
  text.cc text.h) # intermediate source files

# add build dir for project to include directories
include_directories(app 
    $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>)

拥有DEPENDS toolinadd_custom_commandOUTPUTof add_custom_commandinadd_executable授予:

  1. test.txt在 VS 项目中被列为源app
  2. VS-projecttool包含在 VS-solution 中App
  3. tool被编译链接,然后用来转换test.txttest.htest.cc之前app被编译链接(成功)。

生成的中间源出现在构建目录中(不要污染源目录)。因此,构建目录也必须设置为包含路径。否则,#include "text.h"(in app.cc) 将不起作用。


推荐阅读