I have a number of source files in a number of folders.. Is there a way to just compile all of them in one go without having to name them?
I know that I can say
g++ -o out *.cpp
But when I try
g++ -o out *.cpp folder/*.cpp
I get an error.
What's the correct way to do this? I know it's possible with makefiles, but can it be done with just straight g++?
By specifying folder/*.cpp you are telling g++ to compile cpp files in folder. That is correct.
What you may be missing is telling the g++ where to locate additional files that those cpp files #include.
To do this, tell your compiler to also include that directory with -I like this:
g++ -o out -I ./folder *.cpp folder/*.cpp
In some circumstances I have had the compiler forget what was in the root/current directory, so I manually specified it with another -I to the current directory .
g++ -o out -I . -I ./folder *.cpp folder/*.cpp
Figured it out! :) I hate the idea of having to use make or a build system just to compile my code, but I wanted to split up my code into subfolders to keep it clean and organized.
Run this (replace RootFolderName (e.g. with .) and OutputName):
g++ -g $(find RootFolderName -type f -iregex ".*\.cpp") -o OutputName
The find command will do a recursive case-insensitive regex search (-iregex) for files (-type f). Placing this within $() will then inject the results into your g++ call. Beautiful! :)
Of course, make sure you're using the command right; you can always do a test run.
For those using Visual Studio Code to compile, I had to do this in the tasks.json args: [] of a task with "command": "g++":
"-g",
"$(find",
"code",
"-type",
"f",
"-iregex",
"'.*\\.cpp')",
"-o",
"program"
(Otherwise it would wrap the $() all in single quotes.)
(Thanks to: user405725 # https://stackoverflow.com/a/9764119/1599699 comments)
So I ran across this and saw the vscode task above, and managed a different solution. This will get all the headers and c files from the same directory. Then will work with the F5 key.
tasks.json
{
"tasks": [
{
"type": "shell",
"label": "C/C++: gcc-7 build active file",
"command": "/usr/bin/gcc-7",
"args": [
"-I",
"${fileDirname}",
"-g",
"${fileDirname}/*.c",
"-o",
"${fileDirname}/${fileBasenameNoExtension}"
],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
}
}
],
"version": "2.0.0"
}
In case that all files contain their own main and there are not part of the same project/program, you might want to keep their original name and create one executable for each file. If this is the case, you could use awk:
awk '{n=split(FILENAME, a, "."); outfile=sprintf("%s.out",a[n-1]); command=sprintf("g++ -I . %s -o %s", FILENAME, outfile); system(command); nextfile } ' *.cpp
For start, you use split to separate filename and extension and store the result into an array a. The basename is located at index n-1 and the extension is at index n. So, we change the extension into .out and by using sprintf store into the outfile variable. In the same way, we create the command to be executed by system function in a shell.
A shortest version of the above command is:
awk '{n=split(FILENAME, a, "."); command=sprintf("g++ -I . %s -o %s.out", FILENAME, a[n-1]); system(command); nextfile } ' *.cpp
You could also modify the command to #include additional files with -I as suggested by #Bit Fracture.
NOTE: This solution doesn't work well if any filename contains a space or multiple dots. Before executing the command, those characters need to be replaced with an underscore for example, as described here:
find . -type f -name "* *.cpp" -exec bash -c 'mv "$0" "${0// /_}"' {} \;
Related
There are often many swiches to enable/disable when trying to build a project that uses CMake.
How do you store the build settings made by some user to make a build reproduceable on another machine? Is there some kind of export functionality or do you just copy the build (cache) folder?
There is an option to pre-load a script for populating the cache file with cmake using
cmake -C <initial-cache>
The initial-cache is a file containing variables set in the following way, e.g. for the install prefix:
set(CMAKE_INSTALL_PREFIX "/my/install/prefix" CACHE PATH "")
Then just pass this file while populating the cache with cmake. Easy, but I didn't know that and found no good sample. As a plus, this is an platform independent way instead of writing a script or batch file.
I create a separate script folder next to the sources out of the generated out-of-source build folder. My files containing the settings are stored there for each lib/executable to build.
You can put all the settings into a separate file and at the end of the day there are just a few calls left:
cmake -E make_directory build/somelib
cmake -E chdir build/somelib cmake -C ../../script/cmake/somelib.cmake ../../source/somelib/src
cmake --build build/somelib --target install
Simple, isn't it?
Automatically generate initial-cache file:
If you are on a *nix system you can run the following inside your build dir:
cmake -N -LA | tail -n+2 | sed -r 's/([A-Za-z_0-9]+):([A-Z]+)=(.*)/set(\1 "\3" CACHE \2 "")/' >cmake-init.txt
On Windows, something like the following cmake script should work:
# list all cache variables
# this should be run in your build dir
set(filename ${CMAKE_ARGV3})
message(STATUS "Writing to ${filename}")
if(NOT filename)
message(FATAL_ERROR "Must provide an output filename")
return()
endif()
execute_process(COMMAND "${CMAKE_COMMAND}" "-N" "-LA" OUTPUT_VARIABLE cacheVars)
string(REPLACE "\n" ";" cacheVars ${cacheVars})
file(WRITE ${filename} "")
foreach (variable ${cacheVars})
string(REGEX REPLACE "([A-Za-z_0-9]+):([A-Z]+)=(.*)$" "set(\\1 \"\\3\" CACHE \\2 \"\")\n" output ${variable})
if(CMAKE_MATCH_0)
file(APPEND ${filename} ${output})
endif()
endforeach()
Save it to, e.g., get_cache_vars.cmake and run it like:
cd <your build-dir>
cmake -P path\to\get_cache_vars.cmake <outputfile.txt>
The best way to replicate this on another machine is to use -DSETTING=TRUE/FALSE args.
If you have a LOT of these options differing from the default you can build your cmake call using a script.
Ex:
#!/bin/bash
cmake -G "Unix Makefiles \
-DOPTION1=TRUE
-DOPTION2=FALSE
Distribute the helper bash script to the other machine.
CMake 3.19 added support for project- and user-level preset files. For example, if CMakeUserPresets.json in your top-level source directory contained the following
{
"version: 4,
"configurePresets": [
{
"name": "localdev",
"displayName": "Local development",
"description": "Local development",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build",
"cacheVariables": {
"ENABLE_CCACHE": "ON",
"GO_FASTER": "ON",
"MY_LIB_INCLUDEDIR": "/usr/local/my-lib/include",
"MY_LIB_LIBDIR": "/usr/local/my-lib/lib",
},
"environment": {
"CMAKE_PREFIX_PATH": "/usr/local/foo"
}
}
]
}
you could use the "localdev" presets by running cmake --preset localdev. At the time of this writing the format is still changing with each version, but it's more flexible and comprehensive that the -C <initial cache> option.
I'm trying to build some PDFs in a Makefile using Sphinx. The resulting PDF has broken references, so I want to fix those using pdftk.
Goal
So what I want to do for all PDFs I build is this:
# Creates the PDF files.
$(SPHINXBUILD) -b pdf $(ALLSPHINXOPTS) source/pdf/ $(BUILDDIR)/pdf_broken
# Go through all PDFs and fix them.
pdftk $(BUILDDIR)/pdf_broken/thepdf.pdf output $(BUILDDIR)/pdf/thepdf.pdf
Attempt with Make
So to do this with Make I have written this Makefile:
# Build PDF (results in broken references)
$(BUILDDIR)/pdf_broken/%.pdf:
$(SPHINXBUILD) -b pdf $(ALLSPHINXOPTS) source/pdf/ $(BUILDDIR)/pdf_broken
# This fixes the broken pdfs and produces the final result.
$(BUILDDIR)/pdf/%.pdf: $(BUILDDIR)/pdf_broken/%.pdf
mkdir -p $(BUILDDIR)/pdf/
pdftk $^ output $#
pdf: $(BUILDDIR)/pdf/%.pdf
Expected result
I'm using Pattern matching as I understand it from reading the manual:
http://www.tack.ch/gnu/make-3.82/make_91.html
Where $< as I understand it should be the prerequisite expanded so from my above example:
$(BUILDDIR)/pdf_broken/thepdf.pdf
and then $# should be the target:
$(BUILDDIR)/pdf/thepdf.pdf
So my recipe pdftk $^ output $# should run the command:
pdftk $(BUILDDIR)/pdf_broken/thepdf.pdf output $(BUILDDIR)/pdf/thepdf.pdf
Actual result
But this is not what is happening. Instead, this is run:
pdftk build/pdf_broken/%.pdf output build/pdf/%.pdf
Which obviously gives me an error:
Error: Unable to find file.
Error: Failed to open PDF file:
build/pdf_broken/%.pdf
Question
So my question is, what have I missundestood with how the pattern matching works, and how is the correct way to solve this using Make?
You should likely lookup pattern rules. In any case, it looks like you have a single command to generate all the files in the broken directory. This should have its own rule, and should likely output a dummy file to indicate it is complete. Your rule to fix the pdf files should be dependent on this dummy target being created.
It should be something like:
// get a list of expected output files:
PDF_SOURCES:=$(wildcard source/pdf/*)
PDF_OUTS:=$(patsubst $(PDF_SOURCES),source/pdf/%.pdf,$(BUILDDIR)/pdf/%.pdf);
// just for debugging:
$(info PDF_SOURCES = $(PDF_SOURCES))
$(info PDF_OUTS = $(PDF_OUTS))
// default rule
all: $(PDF_OUTS)
#echo done
// rule to build BUILDIR:
$(BUILDDIR)/pdf:
mkdir -p $#
// rule to build all broken files in one go:
// (note: generates a file .dosphynx, which is used to keep track
// of when the rule was run last. This rule will be run if the
// timestamp of any of the sources are newer.
.do_sphynx: $(PDF_SOURCES) | $(BUILDDIR)/pdf
$(SPHINXBUILD) -b pdf $(ALLSPHINXOPTS) source/pdf/ $(BUILDDIR)/pdf_broken
touch $#
// create a dependency of all output files on do_sphynx
$(PDF_OUTS): .do_sphynx
// patern rule to fix pdf files
$(BUILDDIR)/pdf/%.pdf : $(BUILDDIR)/pdf_broken/%.pdf
pdftk $< output $#
I've not tested this, so its possible it may have a syntax error in it..
---------------------- EDIT -------------
Ok, since $(PDF_OUTS) cannot be determined at makefile read time, perhaps you should do:
// get a list of expected output files:
PDF_SOURCES:=$(wildcard source/pdf/*)
all: .do_fix
#echo done
$(BUILDDIR)/pdf:
mkdir -p $#
.do_sphynx: $(PDF_SOURCES) | $(BUILDDIR)/pdf
$(SPHINXBUILD) -b pdf $(ALLSPHINXOPTS) source/pdf/ $(BUILDDIR)/pdf_broken
touch $#
.do_fix: .do_sphynx
#for src in $$(ls source/pdf/*.pdf); do \
trg=$${src/#"source/pdf"/"$(BUILD_DIR)/pdf"}; \
[[ $$src -nt $$trg ]] && \
echo "$$src ==> $$trg" && pdftk $$src output $$trg; \
done
touch $#
One note -- the -nt comparator in the if will return true if $trg does not exist, so it will cover the case where the file is missing, or the target is older than the source. Again not tested, but it should work.
It's my first time doing something with bash-programming. As a first example I'm trying to source my .bashrc from my .bash_profile - even when ~/.bashrc is a symbolic link.
.bash_profile:
if [ -f ~/.bashrc ] && ! [ -L ~/.bashrc ]
then
# ~/.bashrc is a regular file. Source it!
source ~/.bashrc
echo "~/.bashrc found."
elif [ -L ~/.bashrc ]
then
# ~/.bashrc is a symbolic link.
# Recursivly follow symbolic links.
location="~/.bashrc"
while [ -L $location ]
do
# QUESTION: Control-Flow never reaches this point.
# Follow link on macOS.
location="$(readlink $path)"
done
# Check if final target is regular file. Source it!
if [ -f $location ]
then
source $location
echo "Symlink to .bashrc found."
fi
else
echo "No valid .bashrc found."
fi
This is what I expect my code to do:
If ~/.bashrc is not a symbolic link, but a regular file: Source it.
Otherwise if ~/.bashrc is a symbolic link:
Follow the symbolic link as long as the target keeps being a symbolic link.
If the final target is a regular file: Source it.
Otherwise: Give it up.
As a test I created a symbolic link ~/.bashrc to the original file .dotfiles/.bashrc. My code enters the elif as intended, but sadly never enters the body of the while-loop (as I would expect, since ~/.bashrc is a symbolic link).
What is going on here? I think the variable assignment of location is wrong in some way.
Replace:
location="$(readlink $path)"
With:
location="$(readlink $location)"
Notes:
The variable path was never defined. I believe that you intended to apply readlink to location instead
If you had GNU readlink (available on Linux, homebrew, etc), then the option -f could be used eliminating the need for a loop.
In general, shell variables should be referenced inside double-quotes unless one wants the shell to apply word splitting, tilde expansion, and pathname expansion to the value of the variable.
For example, in the following line, we want tilde expansion. So, ~/ needs to be outside of quotes:
location=~/.bashrc
Once this is done, the parts of the script where location is referenced should be in double-quotes. As one example:
location="$(readlink "$location")"
This becomes very important, for example, if file or path names contains spaces.
This causes the problem:
location="~/.bashrc"
The tilde expansion doesn't happen in double quotes, and it doesn't happen in
[ -L $location ]
either.
So, don't use double quotes in the assignment, or use
location="$HOME/.bashrc"
or similar.
All,
I'm trying to modify a Makefile to use a embedded cross compiler instead of the PC's compiler. The Makfile does not have the normal CC or CXX variables. In fact, it appears that is calls another makefile with the variable '#${MAKE}'. How can I override the '#${MAKE}' variable to force the makefile to use a diffent compliler?
Thanks In Advance,
# GNU Make solution makefile autogenerated by Premake
# Type "make help" for usage help
ifndef config
config=debug
endif
export config
PROJECTS := json openjaus
.PHONY: all clean help $(PROJECTS)
all: $(PROJECTS)
json:
#echo "==== Building json ($(config)) ===="
#${MAKE} --no-print-directory -C .build -f json.make
openjaus: json
#echo "==== Building openjaus ($(config)) ===="
#${MAKE} --no-print-directory -C .build -f openjaus.make
I edited the Makefile based on Rob's comments, now I'm receiving the message below, not sure what to do?
make[1]: Nothing to be done for `/home/botbear/openwrt/trunk/staging_dir/toolchain-arm_v6k_gcc-linaro_uClibc-0.9.32_eabi/bin/arm-openwrt-linux-uclibcgnueabi-g++'.
You'd have to look inside json.make and openjaus.make to see how they build programs. If they use the conventional variables, you might be able to do something like:
${MAKE} CC=/usr/bin/gcc-arm CXX=/usr/bin/g++-arm --no-parent-directory ...
I'd like to create a makefile rule to run astyle on any writable source files. Currently, I have a rule like the following:
style:
find . -perm -200 -regex ".*[.][CHch]p*" -exec astyle --suffix=none --style=ansi --convert-tabs "{}" \;
This rule basically works but doesn't seem to be the make way of doing things.
Assuming you have a list of source files (or can create them with the shell function), something like:
style : $(SOURCES:.cpp=.astyle-check-stamp)
astyle $(ASTYLEFLAGS) $< && touch $#
would be the make-style. It would re-check each changed source file with astyle and skipped already checked files.