Permalänk
Medlem

Lära sig Make och CMake?

Har hoppat mellan olika projekt genom åren, och därmed mellan olika plattformar med olika programmeringsspråk. Detta gör att man kan lite av varje. Men såklart, efter 3-4 år så glömmer man ju hur man gjorde. Man får lite "hur var det man gjorde nu igen"-känslan. Nu var det länge sedan jag satte upp ett projekt med Make eller CMake (med gcc/clang) och skulle vilja fräscha upp minnet lite. Vad är enligt er det bästa och snabbaste sättet att dyka in i Make/CMake?

Vet inte exakt vad jag vill göra men jag vill göra ett enkelt projekt som byggs med hjälp av Make och CMake.
Vill egentligen bara sätta upp en enkel projekt-struktur, exempelvis med projektmapp /MyProject och i den mappen finns t.ex. /src, /include, /resource, /lib, etc.

Vill kunna saker såsom: Hämta alla .h/.hpp filer i mappen /include och lagra dessa i en variabel $HEADERS. Länka biblioteken -lXXXX ... från YYYY. Och så vidare.

Ska jag tugga dokumentationen? Kolla Youtube? Böcker? Alla tips uppskattas

Permalänk
Medlem

CMake har jag ingen koll på - enda gången jag läst CMake kod är när jag konverterat/simplifierat projekt från CMake till Make! I min åsikt är CMake ofta onödigt komplicerat jämfört med Make, från vad jag sett hittils.

Ang. Make så har jag personligen lärt mig inkrementellt från främst stackoverflow allteftersom jag stött på problem som behöver nya lösningar. Kanske inte det bästa sättet, men mina projekt bygger och fungerar, så jag är nöjd.

Om det är alls hjälpsamt kan jag bifoga makefilen från mitt senaste C++ projekt - en pathtracer i C++ och Nvidia Optix, Cuda. Den är "bara" 75 rader, så kanske går att plocka upp lite från den:

###################################################################### # Public variables ###################################################################### SHELL = /bin/sh OPTIX = $(HOME)/Documents/Optix CUDA = $(HOME)/Documents/CUDA CPP = clang CUS = pathTracer.cu parallelogram.cu sphere.cu DEBUG = FALSE ###################################################################### # Private variables ###################################################################### NVCC = $(CUDA)/bin/nvcc SDK = $(OPTIX)/SDK NVCC_FLAGS = -std=c++11 CPP_FLAGS = -fPIC -Wall -std=c++17 ifeq ($(DEBUG), TRUE) CPP_FLAGS += -g else CPP_FLAGS += -DNDEBUG -g3 -O3 -msse -msse2 -msse3 -mfpmath=sse -funroll-loops endif CPP_DEFINES = -DIMGUI_IMPL_OPENGL_LOADER_GLEW CPP_INCLUDES = -Iimgui -Iglfw3/include -I$(OPTIX)/include -I$(SDK)/sutil -I$(SDK) -I$(OPTIX)/include/optixu -I$(SDK)/support/mdl-sdk/include -I$(CUDA)/include CPP_LDFLAGS = glfw3/lib64/libglfw3.a $(SDK)/lib/libsutil_sdk.so $(OPTIX)/lib64/liboptix.so -Wl,--disable-new-dtags -lm $(OPTIX)/lib64/liboptixu.so -lXmu -lXi -lGL -lGLU -ldl -lpthread -lX11 $(CUDA)/lib64/libnvrtc.so -lstdc++ PTXS = $(patsubst %.cu,target/%.ptx,$(CUS)) IMGUI_SOURCES = $(wildcard imgui/*.cpp) IMGUI_OBJS = $(patsubst imgui/%.cpp,build/%.o,$(IMGUI_SOURCES)) HEADERS = $(wildcard src/*.h) SOURCES = $(wildcard src/*.cpp) OBJS = $(patsubst src/%.cpp,build/%.o,$(SOURCES)) $(IMGUI_OBJS) ###################################################################### # Targets ###################################################################### .PHONY: default_target default_target: all # The main all target .PHONY: all all: target build target/pathTracer $(PTXS) target/pathTracer: $(OBJS) $(SDK)/lib/libsutil_sdk.so $(CPP) $(CPP_FLAGS) -Wl,-rpath,$(SDK)/lib:$(OPTIX)/lib64:$(CUDA)/lib64 -o target/pathTracer -rdynamic $(OBJS) $(CPP_LDFLAGS) build/%.o: src/%.cpp $(HEADERS) $(CPP) $(CPP_DEFINES) $(CPP_INCLUDES) $(CPP_FLAGS) -c -o $@ $< build/%.o: imgui/%.cpp $(CPP) $(CPP_DEFINES) $(CPP_INCLUDES) $(CPP_FLAGS) -c -o $@ $< $(SDK)/lib/libsutil_sdk.so: make -f $(SDK)/sutil/Makefile target/%.ptx: src/%.cu $(wildcard src/*.cu) $(NVCC) $(NVCC_FLAGS) $(CPP_INCLUDES) -ptx -o $@ $< target: mkdir -p target build: mkdir -p build .PHONY: clean clean: rm -rf build rm -rf target .PHONY: run run: all cd target && ./pathTracer

Jag har gjort något liknande dina exempelproblem om du kollar på raden HEADERS = $(wildcard src/*.h) och raderna med CPP_LDFLAGS.

Visa signatur

Arbets- / Spelstation: Arch Linux - Ryzen 5 3600 - RX 7900 XT - 32G DDR4
Server: Arch Linux - Core i5-10400F - 16G DDR4

Permalänk
Medlem

@Bryal: Grymt! Precis vad jag var ute efter. En Make-fil behöver inte vara 500 rader lång, ett stort projekt kan sättas upp med < 100 rader utan problem.

Stort tack för att du delar med dig av ett eget exempel. Speciellt med CUDA

Permalänk
Medlem
Skrivet av Alotiat:

@Bryal: Grymt! Precis vad jag var ute efter. En Make-fil behöver inte vara 500 rader lång, ett stort projekt kan sättas upp med < 100 rader utan problem.

Precis. Tycker att många verkar "over-engineer"a sina byggsystem för att vara absolut generella och hantera alla möjliga situationer, när en mycket simplare specifik lösning är möjlig.

Skrivet av Alotiat:

Stort tack för att du delar med dig av ett eget exempel. Speciellt med CUDA

Ingen orsak! Lycka till!

Visa signatur

Arbets- / Spelstation: Arch Linux - Ryzen 5 3600 - RX 7900 XT - 32G DDR4
Server: Arch Linux - Core i5-10400F - 16G DDR4

Permalänk
Datavetare
Skrivet av Alotiat:

Har hoppat mellan olika projekt genom åren, och därmed mellan olika plattformar med olika programmeringsspråk. Detta gör att man kan lite av varje. Men såklart, efter 3-4 år så glömmer man ju hur man gjorde. Man får lite "hur var det man gjorde nu igen"-känslan. Nu var det länge sedan jag satte upp ett projekt med Make eller CMake (med gcc/clang) och skulle vilja fräscha upp minnet lite. Vad är enligt er det bästa och snabbaste sättet att dyka in i Make/CMake?

Vet inte exakt vad jag vill göra men jag vill göra ett enkelt projekt som byggs med hjälp av Make och CMake.
Vill egentligen bara sätta upp en enkel projekt-struktur, exempelvis med projektmapp /MyProject och i den mappen finns t.ex. /src, /include, /resource, /lib, etc.

Vill kunna saker såsom: Hämta alla .h/.hpp filer i mappen /include och lagra dessa i en variabel $HEADERS. Länka biblioteken -lXXXX ... från YYYY. Och så vidare.

Ska jag tugga dokumentationen? Kolla Youtube? Böcker? Alla tips uppskattas

Svårt att se varför man inte skulle använda CMake för ett nytt C/C++/CUDA projekt som skapas idag. Kan verka enkelt att svänga ihop sin egen Makefile, men även enkla exempel har typisk flera av dessa problem

  • potentiella race om man använder flera kärnor för att bygga

  • felaktiga beroenden mellan filer, speciellt mellan källkodsfiler och headers. För att få detta rätt måste varje källkodsfil scannas och en speciell byggregel för varje fil måste skapas

  • väldigt komplicerat att få till egna Makefiler som faktiskt fungerar någorlunda på mer än ett OS (varför skulle man inte stödja Linux, MacOS och Windows där det är möjligt?)

  • svårt för andra att veta hur din makefile fungerar, d.v.s. vad är konfigurerbart, vad förutsätts etc

Detta är grunden till en CMakeLists.txt som du frågar efter. Skulle nog själv dela upp det i flera filer, men för mindre projekt behövs kanske inte det

cmake_minimum_required(VERSION 3.10) # C++ and CUDA project project(MyProject VERSION 1.0 LANGUAGES CXX CUDA) # Enable C++17... set(CMAKE_CXX_STANDARD 17) # ...and fail if the compiler do not support C++17 set(CMAKE_CXX_STANDARD_REQUIRED ON) # Enable C++14 for CUDA files... set(CMAKE_CUDA_STANDARD 14) # ...and fail if the CUDA_compiler do not support C++14 set(CMAKE_CUDA_STANDARD_REQUIRED ON) # add /include of this directory to the include path include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) # Make list of all C++ and all CUDA files in /src file(GLOB CPP_SRCS src/*.cpp) file(GLOB CUDA_SRCS src/*.cu) # Build rules for program add_executable(myprog ${CPP_SRCS} ${CUDA_SRCS}) # Potentially add libraries with # target_link_libraries(myprog libx liby...)

Detta fungerar både för Linux och Windows (och MacOS om man nu råkar ha en Nvidia GPU). CMake kommer själv lura ut var CUDA är installerat (inom rimliga gränser). Vill du köra med clang++ + debug kör du (har en väldigt gammal version av clang på denna maskin, kör alltid GCC på just den här maskinen)

$ mkdir debug $ cd debug $ CXX=clang++ cmake -DCMAKE_BUILD_TYPE=Debug .. -- The CXX compiler identification is Clang 6.0.0 -- The CUDA compiler identification is NVIDIA 9.1.85 -- Check for working CXX compiler: /opt/clang+llvm-6.0.0-x86_64-linux-gnu-ubuntu-16.04/bin/clang++ -- Check for working CXX compiler: /opt/clang+llvm-6.0.0-x86_64-linux-gnu-ubuntu-16.04/bin/clang++ -- works -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Detecting CXX compile features -- Detecting CXX compile features - done -- Check for working CUDA compiler: /usr/bin/nvcc -- Check for working CUDA compiler: /usr/bin/nvcc -- works -- Detecting CUDA compiler ABI info -- Detecting CUDA compiler ABI info - done -- Configuring done -- Generating done -- Build files have been written to: /home/kjonsson/programming/cmake/proj/debug $ make -j $(nproc --all)

Edit: Oh, angående vad du bör läsa för att få kläm på CMake. Går att googla.
Problemet är att CMake är väldigt väl dokumenterat, fast bara när man redan förstår grunderna... Ofta rätt svårt att veta vad man ska söka på innan man fått hyfsad kläm på något.

Kan därför rekommendera boken Professional CMAKE - A practical guide, men för det du beskriver ovan kan du garanterat googla dig till det som behövs då det är rätt konkreta frågor samt förutsatt att projektet är ett litet personligt projekt.

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Medlem
Skrivet av Yoshman:

Svårt att se varför man inte skulle använda CMake för ett nytt C/C++/CUDA projekt som skapas idag. Kan verka enkelt att svänga ihop sin egen Makefile, men även enkla exempel har typisk flera av dessa problem

  • potentiella race om man använder flera kärnor för att bygga

  • felaktiga beroenden mellan filer, speciellt mellan källkodsfiler och headers. För att få detta rätt måste varje källkodsfil scannas och en speciell byggregel för varje fil måste skapas

  • väldigt komplicerat att få till egna Makefiler som faktiskt fungerar någorlunda på mer än ett OS (varför skulle man inte stödja Linux, MacOS och Windows där det är möjligt?)

  • svårt för andra att veta hur din makefile fungerar, d.v.s. vad är konfigurerbart, vad förutsätts etc

Detta är grunden till en CMakeLists.txt som du frågar efter. Skulle nog själv dela upp det i flera filer, men för mindre projekt behövs kanske inte det

cmake_minimum_required(VERSION 3.10) # C++ and CUDA project project(MyProject VERSION 1.0 LANGUAGES CXX CUDA) # Enable C++17... set(CMAKE_CXX_STANDARD 17) # ...and fail if the compiler do not support C++17 set(CMAKE_CXX_STANDARD_REQUIRED ON) # Enable C++14 for CUDA files... set(CMAKE_CUDA_STANDARD 14) # ...and fail if the CUDA_compiler do not support C++14 set(CMAKE_CUDA_STANDARD_REQUIRED ON) # add /include of this directory to the include path include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) # Make list of all C++ and all CUDA files in /src file(GLOB CPP_SRCS src/*.cpp) file(GLOB CUDA_SRCS src/*.cu) # Build rules for program add_executable(myprog ${CPP_SRCS} ${CUDA_SRCS}) # Potentially add libraries with # target_link_libraries(myprog libx liby...)

Detta fungerar både för Linux och Windows (och MacOS om man nu råkar ha en Nvidia GPU). CMake kommer själv lura ut var CUDA är installerat (inom rimliga gränser). Vill du köra med clang++ + debug kör du (har en väldigt gammal version av clang på denna maskin, kör alltid GCC på just den här maskinen)

$ mkdir debug $ cd debug $ CXX=clang++ cmake -DCMAKE_BUILD_TYPE=Debug .. -- The CXX compiler identification is Clang 6.0.0 -- The CUDA compiler identification is NVIDIA 9.1.85 -- Check for working CXX compiler: /opt/clang+llvm-6.0.0-x86_64-linux-gnu-ubuntu-16.04/bin/clang++ -- Check for working CXX compiler: /opt/clang+llvm-6.0.0-x86_64-linux-gnu-ubuntu-16.04/bin/clang++ -- works -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Detecting CXX compile features -- Detecting CXX compile features - done -- Check for working CUDA compiler: /usr/bin/nvcc -- Check for working CUDA compiler: /usr/bin/nvcc -- works -- Detecting CUDA compiler ABI info -- Detecting CUDA compiler ABI info - done -- Configuring done -- Generating done -- Build files have been written to: /home/kjonsson/programming/cmake/proj/debug $ make -j $(nproc --all)

Edit: Oh, angående vad du bör läsa för att få kläm på CMake. Går att googla.
Problemet är att CMake är väldigt väl dokumenterat, fast bara när man redan förstår grunderna... Ofta rätt svårt att veta vad man ska söka på innan man fått hyfsad kläm på något.

Kan därför rekommendera boken Professional CMAKE - A practical guide, men för det du beskriver ovan kan du garanterat googla dig till det som behövs då det är rätt konkreta frågor samt förutsatt att projektet är ett litet personligt projekt.

Riktigt snyggt exempel! Ska försöka bygga vidare på det.

Som sagt så vet jag inte vilket som är smidigast att använda, ena är ju mer för cross-plattform medan den andra är statisk(?). Det var länge sedan jag arbetade med dessa verktyg för länk/bygg-miljöer så jag vet inte vad som är enklast idag. Däremot har jag läst någonstans (minns inte var, detta var ett tag sedan) att CMake för version 2.1(?) inte var kompatibelt med CUDA, samt att det skulle vara "klumpigt" att ordna (för vilka plattformar/toolschain minns jag inte...svagt minne...). Men version > 3.0 kanske fungerar mycket bättre?

Tack för boktipset. Den ska jag köpa

Sedan har jag hört lite om Ninja (länk: https://ninja-build.org/). Något som ni rekommenderar? Verkar dock vara ett byggverktyg på låg nivå inte så användarvänligt. Fokus där kanske är prestanda.

Permalänk
Datavetare
Skrivet av Alotiat:

Riktigt snyggt exempel! Ska försöka bygga vidare på det.

Som sagt så vet jag inte vilket som är smidigast att använda, ena är ju mer för cross-plattform medan den andra är statisk(?). Det var länge sedan jag arbetade med dessa verktyg för länk/bygg-miljöer så jag vet inte vad som är enklast idag. Däremot har jag läst någonstans (minns inte var, detta var ett tag sedan) att CMake för version 2.1(?) inte var kompatibelt med CUDA, samt att det skulle vara "klumpigt" att ordna (för vilka plattformar/toolschain minns jag inte...svagt minne...). Men version > 3.0 kanske fungerar mycket bättre?

Tack för boktipset. Den ska jag köpa

Sedan har jag hört lite om Ninja (länk: https://ninja-build.org/). Något som ni rekommenderar? Verkar dock vara ett byggverktyg på låg nivå inte så användarvänligt. Fokus där kanske är prestanda.

Håller själv på att undersöker Ninja och det är nog något vi kommer gå över till på jobbet.

CMake är inte ett byggsystem, det är en generator för ett byggsystem där utdata kan vara t.ex. GNU Make, Visual Studio projekt eller just ett Ninja-projekt.

Ninja har ett par fördelar över vanlig make

  • Ninja finns till Linux, MacOS och Windows. Även om GNU Make finns till Windows är det allt annat än effektivt på den plattformen medan Ninja uppför på lite olika sätt på olika system för att undvika svagheterna i OSet

  • Ninja är väldigt effektivt på att lura ut vad som inte ska byggas. Kan verka meningslöst, men i riktigt stora projekt är just detta ofta en gigantiskt flaskhals vid inkrementella byggen.

Enda du behöver gör för att använda Ninja i ett projekt som använder CMake är att installera Ninja samt köra detta

$ cd BUILD_DIR $ cmake -GNinja .. $ ninja

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer