-
Notifications
You must be signed in to change notification settings - Fork 137
Restructure CMake project layout: make interacting with Artery's options and dependencies easier #382
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All in all, I am in favour of this PR. I have major doubts, however, if separating "dependency finding" from "dependency consuming" is a good idea.
Edit: Up to now, I would say it is okay to have multiple "features" calling the same "find_package". But I agree, there is room for improvement.
@@ -11,14 +11,8 @@ target_include_directories(ots PUBLIC $<TARGET_PROPERTY:core,INCLUDE_DIRECTORIES | |||
set_property(TARGET ots PROPERTY NED_FOLDERS ${CMAKE_CURRENT_SOURCE_DIR}) | |||
set_property(TARGET ots PROPERTY OMNETPP_LIBRARY ON) | |||
|
|||
find_package(PkgConfig MODULE REQUIRED) | |||
pkg_check_modules(ZEROMQ REQUIRED libzmq) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not convinced by this (and similar changes): Before this PR, the "find_package" part is co-located with the "consuming" part. By moving "find_package" away, we have only the usage of ZEROMQ_* variables left but it is not clear where they are defined.
add_subdirectory(networking) | ||
add_subdirectory(application) | ||
|
||
target_sources(core PRIVATE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this part handling sources of each subdirectory separately, nice!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be honest, I wish we had src/artery/core directory and a number of static libs for each core component. I'm sort of terrified by the idea of patching numerous includes, but maybe :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, that separating each component in its own library would be a clean approach. I have considered this in the past but found no clear benefit.
cmake/GenerateOppMessage.cmake
Outdated
|
||
# force cmake to generate message first, in case actual args_TARGET | ||
# sources use results from custom_command (include message headers) | ||
add_custom_target(dummy_${msg_name}_target DEPENDS "${msg_output_header}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, how it managed to work before? :)
There was no guarantee that cmake decides to execute custom_command and generate message source & header file before compiling any other source included to args_TARGET that uses it? I think that OUTPUTS dependency that is created by custom_command exists for target file only, which should imply that cmake may start building other args_target sources before it bumps into non-existent codegen file and invokes our custom_command.
I might be horribly wrong though. How it even happened that refactoring caused this...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh? Why is a dummy target introduced? The generated files are added as sources to the given TARGET and thus the generator command is pulled in by the respective target.
I had no trouble so far adding not-yet-existing sources to a target as long as a matching generation rule exists.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed dummy target so you can observe what happens in CI :)
On my machine, with ninja, I have the following rule to generate a file:
#############################################
# Assume dependencies for generated source file.
build /workspaces/artery/build/Debug/opp_messages/artery/utility/AsioData_m.cc: CUSTOM_COMMAND || cmake_object_order_depends_target_core
restat = 1
Then, here comes object file:
build CMakeFiles/core.dir/opp_messages/artery/utility/AsioData_m.cc.o: CXX_COMPILER__core_unscanned_Debug /workspaces/artery/build/Debug/opp_messages/artery/utility/AsioData_m.cc || cmake_object_order_depends_target_core
CONFIG = Debug
DEFINES = ...
DEP_FILE = CMakeFiles/core.dir/opp_messages/artery/utility/AsioData_m.cc.o.d
FLAGS = -g -std=c++17 -fPIC
INCLUDES = ...
OBJECT_DIR = CMakeFiles/core.dir
OBJECT_FILE_DIR = CMakeFiles/core.dir/opp_messages/artery/utility
TARGET_COMPILE_PDB = CMakeFiles/core.dir/
TARGET_PDB = libartery_core.pdb
Finally, core target:
#############################################
# Link the shared library libartery_core.so
build libartery_core.so: CXX_SHARED_LIBRARY_LINKER__core_Debug ... CMakeFiles/core.dir/src/artery/utility/AsioScheduler.cc.o CMakeFiles/core.dir/src/artery/utility/AsioTask.cc.o CMakeFiles/core.dir/src/artery/utility/Channel.cc.o CMakeFiles/core.dir/src/artery/utility/Identity.cc.o CMakeFiles/core.dir/src/artery/utility/IdentityRegistry.cc.o CMakeFiles/core.dir/src/artery/utility/FilterRules.cc.o CMakeFiles/core.dir/src/artery/utility/Geometry.cc.o CMakeFiles/core.dir/opp_messages/artery/utility/AsioData_m.cc.o ...
CONFIG = Debug
DEP_FILE = CMakeFiles/core.dir/link.d
LANGUAGE_COMPILE_FLAGS = -g
LINK_FLAGS = -Wl,--dependency-file=CMakeFiles/core.dir/link.d
LINK_LIBRARIES = ...
LINK_PATH = -L/omnetpp/lib
OBJECT_DIR = CMakeFiles/core.dir
POST_BUILD = :
PRE_LINK = :
SONAME = libartery_core.so
SONAME_FLAG = -Wl,-soname,
TARGET_COMPILE_PDB = CMakeFiles/core.dir/
TARGET_FILE = libartery_core.so
TARGET_PDB = libartery_core.pdb
You can see that AsioData_m.cc.o comes in the same line as Identity.cc.o (for example), which means that ninja might decide to build Identity.cc.o before AsioData_m.cc.o, when headers for AsioData_m.cc.o have not been generated yet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CI run is here (for future reference): https://github.com/riebl/artery/actions/runs/16848441102/job/47731266081?pr=382
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same build configuration on master yields these:
#############################################
# Custom command for opp_messages/artery/utility/AsioData_m.cc
build opp_messages/artery/utility/AsioData_m.cc opp_messages/artery/utility/AsioData_m.h | ${cmake_ninja_workdir}opp_messages/artery/utility/AsioData_m.cc ${cmake_ninja_workdir}opp_messages/artery/utility/AsioData_m.h: CUSTOM_COMMAND /workspaces/artery/src/artery/utility/AsioData.msg /omnetpp/bin/opp_msgc || extern/libINET.so extern/libveins.so extern/vanetza/lib/libvanetza_access.so extern/vanetza/lib/libvanetza_asn1.so extern/vanetza/lib/libvanetza_asn1_its.so ... /* more shared libs */
COMMAND = cd /workspaces/artery/build/Debug/opp_messages/artery/utility && /omnetpp/bin/opp_msgc -s _m.cc -h /workspaces/artery/src/artery/utility/AsioData.msg
DESC = Generating artery/utility/AsioData
restat = 1
Also, buildorder rule exists:
build cmake_object_order_depends_target_core: phony || cmake_object_order_depends_target_INET cmake_object_order_depends_target_access ... /* more targets */ ... cmake_object_order_depends_target_gnss cmake_object_order_depends_target_net cmake_object_order_depends_target_security cmake_object_order_depends_target_traci cmake_object_order_depends_target_veins opp_messages/artery/utility/AsioData_m.cc opp_messages/artery/utility/AsioData_m.h
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, mystery resolved :)
If I move
generate_opp_message(AsioData.msg TARGET core)
To a different directory other than the one where original TARGET (Artery::core) is defined, it does not generate the command.
So, @riebl, what do you think? Should we force that dummy target to allow such cases, or just declare sub-core targets to actually make cmake generate a rule?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Come to think of it, we should probably adopt the idea of codegen target. This will be useful for linter in CI, and generally feels like a better approach (during development, you may invoke that target to re-generate or generate new auto-generated files)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! I would like to support Debian 12 as a baseline for the moment, which ships with CMake 3.25, i.e. without built-in codegen support.
Since the utility sub-directory contains non-optional sources, we may move their handling back to src/artery/CMakeLists.txt to avoid the issue. A follow-up PR may then add a codegen target.
Can you elaborate on this one, please?)
What do you think? Or we may leave this one out, since there are no guidelines or best practices about it :) |
Well, the built-in features of CMake are rather limited when it comes to managing projects with many optional features and thus variable dependencies. I have seen some projects adding their own dependency management on top. For Artery, I can imagine a lightweight variant. Let me briefly sketch a possible approach:
|
Hey! I think it's implemented now. I did not create declare_artery_dependency, though - CMake has no standard interface to interact with packages, so declaring a package tends to introduce different code each time. |
# Primary Artery targets # | ||
########################## | ||
|
||
# TODO: temporarily targets are moved into src/artery |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why temporarily? What is still "to-do" here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved because of that generate_opp_message issue, but I wish to move them back once codegen target appears and there won't be any requirements for main targets' placement.
We should probably also make Find* configs for some extern packages - this will help integrating them with our new approach to handle deps. |
Hey, @riebl! Any updates on this one?) |
I also added all features to CI, current runs will fail since package rebuild is required. |
Looks all good now, but I don't want do override the PR checks. Would you mind adding the required Dockerfile adjustments to another preparatory PR? |
cb38424
to
d8d4d46
Compare
After small adjustments to package config - CI should pass alright now. :) |
Hey, @riebl! Something odd happens to protobuf-dependent features (testbed and transfusion). I'll investigate that later alongside implementing global codegen target. This PR has grown quite big :) Think we can merge it now. |
Hey, @riebl!
I think we should reconsider our project CMake layout. I can mention the following issues from my personal experience:
I propose: