如果我必须通过外部工具生成该库的源和 CMakeLists.txt 文件,我如何在 cmake-base 项目中使用库
How can I use library in cmake-base project if sources and CMakeLists.txt file for this library I have to generate by external tool
我必须通过外部工具生成源代码(我从 IDL 文件为 fastDDS 消息生成 c++ 类),该工具还生成 CMakeLists.txt 文件,允许我将生成的文件编译为 < msgs_lib>.一个文件.
在我的一个执行目标的大型超级项目中,我不会检查生成的文件及其构建结果是否存在。如果 .a 不存在,我将调用生成并构建它。
我知道 CMake 命令 add_custom_command
和 add_custom_target
。但是我不能以正确的方式使用它们,在某些情况下,即使所有文件都存在并且它们是按正确的顺序构建的,在某些情况下也不会生成任何内容(并且在清理后也不会生成任何内容),在某些情况下 exec-target 会不明白它应该检查并调用生成。
比如上面我写一个案例来展开讨论。我已经简化了很多:
github 上的项目:
https://github.com/gladijos/test_project
一个 zip 文件中的项目:
mediaDrive-link
或者就在这里:
项目目录树:
.
├── CMakeLists.txt
├── deps
│ ├── gen_deps
│ └── gen_source
│ └── tb_msgs
│ ├── CMakeLists.txt
│ ├── tb_msg.cpp
│ └── tb_msg.h
├── scripts
│ └── build_msgs.sh
└── test_app
├── CMakeLists.txt
└── main.cpp
root-CMakeLists.txt:
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
project(test_project LANGUAGES CXX VERSION 0.0.3)
set(TB_MSGS ${CMAKE_HOME_DIRECTORY}/deps/gen_deps/tb_msgs/build/libtb_msgs_lib.a)
add_custom_command(
OUTPUT ${TB_MSGS}
WORKING_DIR ${CMAKE_HOME_DIRECTORY}
COMMAND ${CMAKE_HOME_DIRECTORY}/scripts/build_msgs.sh ${CMAKE_HOME_DIRECTORY}
)
add_custom_target(build_dds_msgs
# ALL
DEPENDS ${TB_MSGS}
)
# apps
add_subdirectory(test_app)
add_dependencies(test_app build_dds_msgs)
tb_msgs CMakeLists.txt:
cmake_minimum_required(VERSION 3.5.1)
project("tb_lib")
set(CMAKE_CXX_STANDARD 11)
add_library(tb_msgs_lib
tb_msg.cpp
)
tb_msg.cpp:
#include "tb_msg.h"
MyClass::MyClass(){};
tb_msg.h:
#pragma once
class MyClass
{
public:
MyClass();
};
build_msgs.sh:
cp -rf /deps/gen_source/* /deps/gen_deps/;
cd /deps/gen_deps/tb_msgs;
mkdir -p /deps/gen_deps/tb_msgs/build;
cd /deps/gen_deps/tb_msgs/build;
cmake .. ;
make;
test_app CMakeListst.txt:
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
project(test_app LANGUAGES CXX VERSION 0.0.1)
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME}
${CMAKE_HOME_DIRECTORY}/deps/gen_deps/tb_msgs/build/libtb_msgs_lib.a
)
test_app main.cpp:
#include "../deps/gen_deps/tb_msgs/tb_msg.h"
int main()
{
MyClass myClass;
return 0;
}
在这种情况下,即使 libtb_msgs_lib.a
存在并且是在 test_app
.
之前构建的,我都会在每次构建时调用 scripts/build_msgs.sh
再来一张。在 add_custom_command
OUTPUT
部分,我放了 .a
,但在最好的情况下,这里应该是目录 gen_source/* 中的所有文件,但我认为这是另一个问题,我可以在之后咬它解决主要问题。
为了解决您的问题,我建议不要依赖外部工具的 cmake 脚本,而是管理 IDL 文件的生成并构建库。我假设您的项目有一个包含 IDL 文件的文件夹,我们称它为 idl
。在此文件夹中还放置一个 CMakeLists.txt
文件来生成和构建库。
项目结构变成如下:
.
├── CMakeLists.txt
├── idl
│ ├── CMakeLists.txt
│ ├── Bar.idl
│ ├── Foo.idl
│ └── MyClass.idl
├── scripts
│ └── fastmsg.sh
└── test_app
├── CMakeLists.txt
└── main.cpp
在此示例中,文件 idl/Bar.idl
、idl/Foo.idl
和 idl/MyClass.idl
可以为空,只有它们的存在很重要。
CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(test_project LANGUAGES CXX VERSION 0.0.3)
set(GENERATOR ${CMAKE_SOURCE_DIR}/scripts/fastmsg.sh)
# apps
add_subdirectory(idl)
add_subdirectory(test_app)
idl/CMakeLists.txt
对于 IDL_LIST
中的每个文件,调用外部工具(GENERATOR
来自根 CMakeLists.txt)来生成 cpp
和 h
文件。 cpp
文件的结果列表用于构建 build_dds_msgs
库。
cmake_minimum_required(VERSION 3.0)
set(IDL_LIST MyClass.idl Foo.idl Bar.idl)
set(IDL_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(CPP_DIR ${CMAKE_CURRENT_BINARY_DIR})
foreach(IDL_FILE ${IDL_LIST})
string(REPLACE ".idl" ".cpp" CPP_FILE ${IDL_FILE})
string(REPLACE ".idl" ".h" HDR_FILE ${IDL_FILE})
list(APPEND CPP_LIST ${CPP_DIR}/${CPP_FILE})
add_custom_command(
OUTPUT ${CPP_DIR}/${CPP_FILE} ${CPP_DIR}/${HDR_FILE}
COMMAND ${GENERATOR} ${IDL_DIR}/${IDL_FILE}
WORKING_DIRECTORY ${CPP_DIR}
DEPENDS ${IDL_DIR}/${IDL_FILE}
VERBATIM
)
endforeach()
add_library(build_dds_msgs ${CPP_LIST})
target_include_directories(build_dds_msgs PUBLIC ${CPP_DIR})
# Add dependencies on an external instrument, if any.
# target_include_directories(build_dds_msgs PRIVATE include_from_gen)
# target_link_libraries(build_dds_msgs PRIVATE libs_from_gen)
test_app/CMakeLists.txt & test_app/main.cpp
这里的变化很小,主要是风格上的变化。
cmake_minimum_required(VERSION 3.0)
project(test_app LANGUAGES CXX VERSION 0.0.1)
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} build_dds_msgs)
#include "MyClass.h"
int main()
{
MyClass myClass;
return 0;
}
scripts/fastmsg.sh
这是真实发电机的模拟器。它需要一个 FileName.idl
并创建 FileName.h
和 FileName.cpp
个空文件 FileName
class.
#!/bin/bash
if [ -z "" ]
then
echo "Usage: `basename [=15=]` filename"
exit 1
fi
FILE=$(basename -- "")
CLASS=${FILE%.*}
cat > $CLASS.h <<EndHpp
#pragma once
class $CLASS
{
public:
$CLASS();
};
EndHpp
cat > $CLASS.cpp <<EndCpp
#include "$CLASS.h"
$CLASS::$CLASS(){};
EndCpp
exit 0
我必须通过外部工具生成源代码(我从 IDL 文件为 fastDDS 消息生成 c++ 类),该工具还生成 CMakeLists.txt 文件,允许我将生成的文件编译为 < msgs_lib>.一个文件.
在我的一个执行目标的大型超级项目中,我不会检查生成的文件及其构建结果是否存在。如果
我知道 CMake 命令 add_custom_command
和 add_custom_target
。但是我不能以正确的方式使用它们,在某些情况下,即使所有文件都存在并且它们是按正确的顺序构建的,在某些情况下也不会生成任何内容(并且在清理后也不会生成任何内容),在某些情况下 exec-target 会不明白它应该检查并调用生成。
比如上面我写一个案例来展开讨论。我已经简化了很多:
github 上的项目: https://github.com/gladijos/test_project
一个 zip 文件中的项目: mediaDrive-link
或者就在这里:
项目目录树:
.
├── CMakeLists.txt
├── deps
│ ├── gen_deps
│ └── gen_source
│ └── tb_msgs
│ ├── CMakeLists.txt
│ ├── tb_msg.cpp
│ └── tb_msg.h
├── scripts
│ └── build_msgs.sh
└── test_app
├── CMakeLists.txt
└── main.cpp
root-CMakeLists.txt:
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
project(test_project LANGUAGES CXX VERSION 0.0.3)
set(TB_MSGS ${CMAKE_HOME_DIRECTORY}/deps/gen_deps/tb_msgs/build/libtb_msgs_lib.a)
add_custom_command(
OUTPUT ${TB_MSGS}
WORKING_DIR ${CMAKE_HOME_DIRECTORY}
COMMAND ${CMAKE_HOME_DIRECTORY}/scripts/build_msgs.sh ${CMAKE_HOME_DIRECTORY}
)
add_custom_target(build_dds_msgs
# ALL
DEPENDS ${TB_MSGS}
)
# apps
add_subdirectory(test_app)
add_dependencies(test_app build_dds_msgs)
tb_msgs CMakeLists.txt:
cmake_minimum_required(VERSION 3.5.1)
project("tb_lib")
set(CMAKE_CXX_STANDARD 11)
add_library(tb_msgs_lib
tb_msg.cpp
)
tb_msg.cpp:
#include "tb_msg.h"
MyClass::MyClass(){};
tb_msg.h:
#pragma once
class MyClass
{
public:
MyClass();
};
build_msgs.sh:
cp -rf /deps/gen_source/* /deps/gen_deps/;
cd /deps/gen_deps/tb_msgs;
mkdir -p /deps/gen_deps/tb_msgs/build;
cd /deps/gen_deps/tb_msgs/build;
cmake .. ;
make;
test_app CMakeListst.txt:
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
project(test_app LANGUAGES CXX VERSION 0.0.1)
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME}
${CMAKE_HOME_DIRECTORY}/deps/gen_deps/tb_msgs/build/libtb_msgs_lib.a
)
test_app main.cpp:
#include "../deps/gen_deps/tb_msgs/tb_msg.h"
int main()
{
MyClass myClass;
return 0;
}
在这种情况下,即使 libtb_msgs_lib.a
存在并且是在 test_app
.
再来一张。在 add_custom_command
OUTPUT
部分,我放了 .a
,但在最好的情况下,这里应该是目录 gen_source/* 中的所有文件,但我认为这是另一个问题,我可以在之后咬它解决主要问题。
为了解决您的问题,我建议不要依赖外部工具的 cmake 脚本,而是管理 IDL 文件的生成并构建库。我假设您的项目有一个包含 IDL 文件的文件夹,我们称它为 idl
。在此文件夹中还放置一个 CMakeLists.txt
文件来生成和构建库。
项目结构变成如下:
.
├── CMakeLists.txt
├── idl
│ ├── CMakeLists.txt
│ ├── Bar.idl
│ ├── Foo.idl
│ └── MyClass.idl
├── scripts
│ └── fastmsg.sh
└── test_app
├── CMakeLists.txt
└── main.cpp
在此示例中,文件 idl/Bar.idl
、idl/Foo.idl
和 idl/MyClass.idl
可以为空,只有它们的存在很重要。
CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(test_project LANGUAGES CXX VERSION 0.0.3)
set(GENERATOR ${CMAKE_SOURCE_DIR}/scripts/fastmsg.sh)
# apps
add_subdirectory(idl)
add_subdirectory(test_app)
idl/CMakeLists.txt
对于 IDL_LIST
中的每个文件,调用外部工具(GENERATOR
来自根 CMakeLists.txt)来生成 cpp
和 h
文件。 cpp
文件的结果列表用于构建 build_dds_msgs
库。
cmake_minimum_required(VERSION 3.0)
set(IDL_LIST MyClass.idl Foo.idl Bar.idl)
set(IDL_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(CPP_DIR ${CMAKE_CURRENT_BINARY_DIR})
foreach(IDL_FILE ${IDL_LIST})
string(REPLACE ".idl" ".cpp" CPP_FILE ${IDL_FILE})
string(REPLACE ".idl" ".h" HDR_FILE ${IDL_FILE})
list(APPEND CPP_LIST ${CPP_DIR}/${CPP_FILE})
add_custom_command(
OUTPUT ${CPP_DIR}/${CPP_FILE} ${CPP_DIR}/${HDR_FILE}
COMMAND ${GENERATOR} ${IDL_DIR}/${IDL_FILE}
WORKING_DIRECTORY ${CPP_DIR}
DEPENDS ${IDL_DIR}/${IDL_FILE}
VERBATIM
)
endforeach()
add_library(build_dds_msgs ${CPP_LIST})
target_include_directories(build_dds_msgs PUBLIC ${CPP_DIR})
# Add dependencies on an external instrument, if any.
# target_include_directories(build_dds_msgs PRIVATE include_from_gen)
# target_link_libraries(build_dds_msgs PRIVATE libs_from_gen)
test_app/CMakeLists.txt & test_app/main.cpp
这里的变化很小,主要是风格上的变化。
cmake_minimum_required(VERSION 3.0)
project(test_app LANGUAGES CXX VERSION 0.0.1)
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} build_dds_msgs)
#include "MyClass.h"
int main()
{
MyClass myClass;
return 0;
}
scripts/fastmsg.sh
这是真实发电机的模拟器。它需要一个 FileName.idl
并创建 FileName.h
和 FileName.cpp
个空文件 FileName
class.
#!/bin/bash
if [ -z "" ]
then
echo "Usage: `basename [=15=]` filename"
exit 1
fi
FILE=$(basename -- "")
CLASS=${FILE%.*}
cat > $CLASS.h <<EndHpp
#pragma once
class $CLASS
{
public:
$CLASS();
};
EndHpp
cat > $CLASS.cpp <<EndCpp
#include "$CLASS.h"
$CLASS::$CLASS(){};
EndCpp
exit 0