admin管理员组

文章数量:1279124

In the last few weeks, I've been experimenting for the first time with C++20 modules after reading about them for years. I extensively worked with them in Visual Studio and I did some successful test with cmake (using nmake as generator) and had little to no issue setting up the project.

Now I have to switch to an environment where only GCC and GNU make are available. I have an old project that I want to migrate to using modules, whose current GNUmakefile looks like this:

SOURCE_EXTENSION := cpp
SOURCES := $(shell find $(SOURCE_DIRECTORY) -type f -name *.$(SOURCE_EXTENSION))
OBJECTS := $(patsubst $(SOURCE_DIRECTORY)/%,$(OBJECT_DIRECTORY)/%,$(SOURCES:.$(SOURCE_EXTENSION)=.o))
COMPILATION_FLAGS := -g -Wall

$(TARGET): $(OBJECTS)
    @mkdir -p $(BINARY_DIRECTORY)
    $(GCC) $^ -o $(BINARY_DIRECTORY)/$(TARGET)

$(OBJECT_DIRECTORY)/%.o: $(SOURCE_DIRECTORY)/%.$(SOURCE_EXTENSION)
    @mkdir -p $(dir $@)
    $(GCC) $(COMPILATION_FLAGS) $(INCLUDES) -c -o $@ $<

Which is a fast way to build everything in my code folder, creating an object folder with a mirroring structure and then linking everything together. I was wondering if and how you could do the same thing with modules, given that they seem very sensible to ordering, so that I don't have to list every single module, manually managing its dependencies.

In the last few weeks, I've been experimenting for the first time with C++20 modules after reading about them for years. I extensively worked with them in Visual Studio and I did some successful test with cmake (using nmake as generator) and had little to no issue setting up the project.

Now I have to switch to an environment where only GCC and GNU make are available. I have an old project that I want to migrate to using modules, whose current GNUmakefile looks like this:

SOURCE_EXTENSION := cpp
SOURCES := $(shell find $(SOURCE_DIRECTORY) -type f -name *.$(SOURCE_EXTENSION))
OBJECTS := $(patsubst $(SOURCE_DIRECTORY)/%,$(OBJECT_DIRECTORY)/%,$(SOURCES:.$(SOURCE_EXTENSION)=.o))
COMPILATION_FLAGS := -g -Wall

$(TARGET): $(OBJECTS)
    @mkdir -p $(BINARY_DIRECTORY)
    $(GCC) $^ -o $(BINARY_DIRECTORY)/$(TARGET)

$(OBJECT_DIRECTORY)/%.o: $(SOURCE_DIRECTORY)/%.$(SOURCE_EXTENSION)
    @mkdir -p $(dir $@)
    $(GCC) $(COMPILATION_FLAGS) $(INCLUDES) -c -o $@ $<

Which is a fast way to build everything in my code folder, creating an object folder with a mirroring structure and then linking everything together. I was wondering if and how you could do the same thing with modules, given that they seem very sensible to ordering, so that I don't have to list every single module, manually managing its dependencies.

Share Improve this question edited Feb 24 at 10:24 prapin 6,8985 gold badges29 silver badges54 bronze badges asked Feb 24 at 7:22 Andrea BoccoAndrea Bocco 9746 silver badges16 bronze badges 11
  • Could you use CMake or build2? – Jarod42 Commented Feb 24 at 8:50
  • Aside: I would not do the same thing with modules, because the Makefile (which appears to be incomplete) does unnecessary work, especially redundant linkages. If you have time for experimenting, the Makefile could do with some of it to rectify its anti-Make nature - executing commands to no purpose. – Mike Kinghan Commented Feb 24 at 9:01
  • @Jarod42 I can use a portable version of CMake, but all generators available do not support modules. About build2, I never used it, but it looks like I wouldn't be able to make it work in my current environment – Andrea Bocco Commented Feb 24 at 9:57
  • 1 $(TARGET)is never made by its recipe so $(BINARY_DIRECTORY)/$(TARGET) will be re-linked when there is no need. The right target is $(BINARY_DIRECTORY)/$(TARGET). mkdir -p ... is fine but not every time a file is made and is foisting superflous makes of a target's prerequisite into its own recipe. $(BINARY_DIRECTORY) and $(OBJECT_DIRECTORY) are actually order-only prerequisites of the targets they are conflated with and if distinguished as such would only be updated when they don't exist. – Mike Kinghan Commented Feb 24 at 12:29
  • 1 This problem is quite complex topic. Please google cmake modules Bill Hoffman, he is explaining how modules are introducing dependency relation between sources, which was previously hidden by header files. This means that simple recipe: object file depends on respective source file; is not enough. – Marek R Commented Feb 24 at 17:22
 |  Show 6 more comments

1 Answer 1

Reset to default 0

Here's an example of a build with GCC using the command line as a starting reference. The module interface (MIU) and implementation units are American.Bobtail.cppm and american_bobtail.cpp, respectively. main.cpp is obvious.

Command Lines

+ g++ -std=c++23 -fmodules-ts -x c++ American.Bobtail.cppm -o bin/American.Bobtail.o -c

+ g++ -std=c++23 -fmodules-ts -c american_bobtail.cpp -o bin/American.Bobtail.All.o

+ g++ -std=c++23 -fmodules-ts -c main.cpp -o bin/main.o
+ g++ -std=c++23 -fmodules-ts bin/main.o bin/American.Bobtail.All.o -o bin/main
+ bin/main
American Bobtail cat says 'meow'

The output looks like:

├── bin
│   ├── American.Bobtail.o
│   ├── american_bobtail.o
│   ├── main
│   └── main.o
├──gcm.cache
   └── American.Bobtail.gcm

Make

I don't know make enough to present it, but I will show CMake below.

There are additional dependencies that make would need to deal with. CMake does not support make, only Ninja.

GCC puts the build of a module with a .gcm extension into the directory gcm_cache. Make would need to check if this exists for any modules. The first line above also puts the binary for the MIU in the bin directory.

The following line builds the implementation unit and links its output with the MIU to build the All binary.

Then, main is built and linked with the All binary. I suggest you start with this example to create a make file and then work to see if it will handle your project.

CMake Build

I don't want to edit much so I have to provide the parent directory and the subdirectory CMake files:

Parent

cmake_minimum_required(VERSION 3.30.5) # can be 28

project(my_modules LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_SCAN_FOR_MODULES ON)
set(CMAKE_CXX_EXTENSIONS OFF)

add_subdirectory(American.Bobtail)

Subdirectory

cmake_minimum_required(VERSION 3.30.5)
project(American.Bobtail LANGUAGES CXX)
set(module_name american_bobtail)

add_executable(${PROJECT_NAME} main.cpp)
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_23)
target_link_libraries(${PROJECT_NAME} ${module_name})

add_library(${module_name})
target_compile_features(${module_name} PUBLIC cxx_std_23)
target_sources(${module_name}
               PUBLIC
               FILE_SET cxx_modules TYPE CXX_MODULES
               FILES ${PROJECT_NAME}.cppm
               PRIVATE ${module_name}.cpp
)

# command line build
# rm -rf build && cmake -B build -G Ninja  && cmake --build build --verbose && build/American.Bobtail

I won't go through all this, but if you get lost, ask in the comments. The commented-out line at the end will be built using CMake from the parent directory.

本文标签: cAutomatic GNU make with C20 moduleStack Overflow