Building a C++ formula for homebrew - c++

I have very very little experience with Ruby and I was trying to build a simple homebrew formula. I had a simple test project with the following structure
.
├── Makefile
└── test.cpp
0 directories, 2 files
And then I have the following .rb formula file
class Testbrew < Formula
desc ""
homepage ""
url ""
version ""
head ""
sha256 ""
def install
# system "make"
# system("g++ -std=c++14 -O3 test.cpp -o testbrew")
system "g++", "-std=c++14",
"-O3",
"-Wall",
"-Werror",
"-Wextra",
"-pedantic",
"-Wvla",
"test.cpp",
"-o testbrew"
bin.install "testbrew"
ohai("Done!")
end
end
The confusing part here is that the first two commented lines of system work to install the package but the third does not, any idea why? If I try the third line I get the error
==> g++ -std=c++14 -O3 -Wall -Werror -Wextra -pedantic -Wvla test.cpp -o testbrew
Error: No such file or directory - testbrew
Also as a followup, are there certain things that the function call system() does not allow the user to do? i.e. are there any security restrictions imposed (for example with ptrace)?

Your argument "-o testbrew" is wrong. That parses as -o with the option " testbrew" including the space. This is only possible because system with mutiple arguments bypasses the normal shell parsing, you're directly responsible for correctly splitting arguments.
You've broken the other arguments out correctly. I'd recommend doing it that way:
"-o", "testbrew"

Related

How the forcing rebuild in my Makefile actually work?

How to force make to always rebuild a file from this answer more specifically, I was able to achieve my goal as a beginner, so I better commented on everything.
I have these 4 files in one directory; ls -F:
iterator Makefile test* test.cpp
where all the files should be self-explanatory, but I have some little feeling, the iterator may sound a bit odd, so just to be crystal clear, I want to store/show the information on how many times I recompiled the test.cpp source code.
Here I would politely like to ask HOW the forcing of rebuild/recompile actually works? I am no expert on Makefiles so that about sums it up.
Here is my actual Makefile with no changes at all:
CXX := g++-10
CXXFLAGS := -O2 -std=c++20 -Wall -Wextra -Werror -Wpedantic -pedantic-errors
APP_NAME := test
SRC_FILE := $(APP_NAME).cpp
# The following works!
# However, I have no idea how? :(
$(APP_NAME): .force_rebuild
.PHONY: .force_rebuild
$(APP_NAME): $(SRC_FILE)
# quickly delete terminal buffer
#clear
# increase test number
#expr $$(cat iterator) + 1 > iterator
# print test number description
#printf '%s' "Test Nr.: "
# set bright cyan color for the number
#tput bold; tput setaf 6
# print test number
#cat iterator
# reset to normal terminal color
#tput sgr0
# compile (always force rebuild)
#$(CXX) $(CXXFLAGS) $(SRC_FILE) -o $(APP_NAME)
# run my test app
#./$(APP_NAME)
For completeness, I work on Linux Mint 20.2 Cinnamon with Bash as shell and VS Code as a text editor using GNU Make 4.2.1.
Side note: It looks weird and less readable when without syntax highlight, which is the only why I am attaching a screenshot also:
From the manual:
One file can be the target of several rules. All the prerequisites mentioned in all the rules are merged into one list of prerequisites for the target. If the target is older than any prerequisite from any rule, the recipe is executed.
In your case you have two rules for the app target. The prerequisite .force_rebuild from the first rule is marked as .PHONY, which makes your app target always older than .force_rebuild. This triggers execution of the recipe for the app target. That recipe is in the second rule.
Just in case, also pointing out the paragraph following the above quote:
There can only be one recipe to be executed for a file. If more than one rule gives a recipe for the same file, make uses the last one given and prints an error message.

Building precompiled header with GN

I'm trying to write a GN file that generates .gch files. I've gone through the GN documentation here
I just don't understand how to implement this, i am novice when it comes to GN. So let me explain what i'm trying to achieve.
I want GN to create the following pair of compiler commands:
my-clang -x c++-header pch/hello_world.h -o pch/hello_world.h.gch
my-clang -include pch/hello_world.h -o hello.out -c hello_world.cpp
I expected to be able to create a source set in my GN file like this:
source_set("source_set0") {
precompiled_source = "//pch/hello_world.h"
precompiled_header = "../pch/hello_world.h"
cflags = [
"-include$precompiled_header"
]
sources = [
"//hello_world.cpp",
]
}
This however does not actually generate the pre-compiled header.
I expect I need to have another source_set or equivalent to specifically compile the header file, however my understanding is that the .gch suffix is required for the compiler to recognise the file as a pre-compiled header.
So i figured that i'd need to convince GN to create the specific output file based on the target
hello_world.h file output hello_world.h.gch
hello_world.cpp output hello_world.o
Ideally i'd like to be able to modify my tool specification:
tool("cxx") {{
command = "\"{cxx_exe}\" ... -c {{{{source}}}} -o {{{{output}}}}"
outputs = [
"{{{{source_out_dir}}}}/{{{{target_output_name}}}}.{{{{source_name_part}}}}.o",
**SOME CONDITION**
"{{{{source_out_dir}}}}/{{{{target_output_name}}}}.{{{{source_name_part}}}}.h.gch",
]
}}
However my reading of Tool Variables indicates that the above functionality is only available for linker_tools.
It seems as well that GN needs some convincing to associate cxx tool with .h files.
Has anyone got any experience with this or able to point me in the right direction.
TL;DR
I want to compiler header files with GN to generated .gch file
I want to use the gch in later step of compile
Following the documentation didn't help, am i misunderstanding
How to convince GN to compiler .h files
Is there i need to make sure that the compilation of the header file happens first
Thanks for your time
p.s.
cpp file contents:
int main (void){
const char* greeting = "Hello world";
print_greeting(greeting);
}
.h file contents
#pragma once
#include <iostream>
void print_greeting(const char* greeting){
std::printf("%s", greeting);
}
I got to the bottom of this, I modified the tool:
tool("cxx") {{
command = "\"{cxx_exe}\" ... -c {{{{source}}}} -o {{{{output}}}}"
precompiler_header_type = "gcc"
outputs = [
"{{{{source_out_dir}}}}/{{{{target_output_name}}}}.{{{{source_name_part}}}}.o",
**SOME CONDITION**
"{{{{source_out_dir}}}}/{{{{target_output_name}}}}.{{{{source_name_part}}}}.h.gch",
]
}}
Then update my gn file:
config("precompiled_header"){
precompiled_header="../pch/hello_world.h"
precompiled_source="//pch/hello_world.h"
}
source_set("source_set1") {
public_configs = [
":precompiled_header"
]
sources = [
"//hello_world.cpp",
]
}
In hindsight this is actually what the documentation said to do. The command lines that are created by GN now look like this
ninja.exe -t commands
"my-clang++.exe" ... -O2 -g -x c++-header -c ../pch/hello_world.h -o obj/pch/source_set1.hello_world.h-cc.gch
"my-clang++.exe" ... -O2 -g -include obj/pch/source_set1.hello_world.h-cc -c ../hello_world.cpp -o obj/source_set1.hello_world.o
Hope this helps someone in the future

How to configure VSCode task to compile multiple files on Linux?

I have a project to compile in VSCode that consists of multiple source files and want to add a single argument to my task that would tell g++ to link all files with .cpp extension. The following does not work:
"args": [
"${workspaceFolder}/*.cpp*",
...
g++: error: /home/kirusfg/Study/Programming/CSCI 152/Assignments/Assignment 4/*.cpp*: No such file or directory,
even though
g++ '/home/kirusfg/Study/Programming/CSCI 152/Assignments/Assignment 4/*.cpp*' -o main-optimized.out -g -O5 -flto -Wreturn-type -pedantic -pedantic-errors -Wundef -std=c++17
works perfectly if entered via terminal.
Seems to me you are having issues with space in your 'workspaceFolder' path, "args" field not handling spaces well.
It seems like your best option is to add the arg to the "command" field.
i.e.:
...
"command": "g++ ${workspaceFolder}/*.cpp*",
"args":[],
...
I hope it will fix your issue.
Reference:
See closed issue from VScode GitHub: How to pass arguments with space to tasks correctly

Create Makefile to be used in different environments for C++

I would like to compile and run my program in two different environments. The libraries in both environments are installed on slightly different places, resulting in different makefile-lines:
In makefile A:
CXXFLAGS=-I$(DIR) -flto -fopenmp -O3 -g -march=native -std=gnu++17 -c -I/opt/interp2d/include -std=c++17 -I/opt/splinter/include -I/usr/include/eigen3
In makefile B:
CXXFLAGS=-I$(DIR) -nostindc++ -I~/local_opt/eigen/include/eigen3/ -I~/local_opt/boost/include -I~/local_opt/armadillo/include -flto -fopenmp -O3 -g -march=native -std=gnu++17 -c -I~/local_opt/interp2d/include -std=c++17 -I~/local_opt/splinterp/include -I/usr/include/eigen3
My problem now is that I am developing the program on the first machine, using makefile A, but also deploying it on the second machine. The deployment is done using git.
Every time I do a git pull on the second machine, I have to fix all the paths in the makefile in order to compile the program properly. Nevertheless I still would like to include the makefile in the git repository in order to keep both makefiles at the same level regarding compiling flags and linked libraries.
Thus, is there an easier way to still sync the makefile via git, while using different paths for the libraries and includes?
I think you could solve your problem by conditionally setting the variable CXXFLAGS in a common file (e.g.: config.mk) and by including that file in your makefiles.
The value used for setting the CXXFLAGS variable could, for example, depend on the value of the environment variable HOST:
ifeq ($(HOST),A)
CXXFLAGS = ... # for machine A
else # B
CXXFLAGS = ... # for machine B
endif
Then, include this config.mk makefile in both makefileA and makefileB:
include config.mk
I like this answer, however, I thought I'd mention this for completeness: If you have a lot of different hosts you can do something to the effect of:
include HostConfig_$(HOST).mk
And then create HostConfig_A.mk and HostConfig_B.mk which set host specific flags (Be it directories, etc). This is useful if you are managing a large project with lots of different host-specific variables.
As well, (for smaller projects), you could do something to the effect of:
CXX_INCLUDES_A = ...
CXX_INCLUDES_B = ...
CXX_FLAGS := -I$(DIR) -flto -fopenmp -O3 -g -march=native -std=gnu++17
CXX_FLAGS += $(CXX_INCLUDES_$(HOST))
The traditional answer to this problem is a configure script (see automake, autoconf for widely used framework). After checking out the source you run ./configure --with-eigen=~/local_opt/eigen/include/eigen3/ and it will adjust your Makefiles accordingly (usually generates Makefile from Makefile.in and only Makefile.in is in git).
Note: Properly done you only need to run configure on the first checkout, not on updates. make can generate Makefile again automatially as needed.

Why the success of SCons build depends on variant_dir name?

I am bored to death with such behavior. So in SConstruct file we have the last string like this one:
import compilers, os
env = Environment(ENV = os.environ, TOOLS = ['default'])
def set_compiler(compiler_name):
env.Replace(FORTRAN = compiler_name)
env.Replace(F77 = compiler_name)
env.Replace(F90 = compiler_name)
env.Replace(F95 = compiler_name)
def set_flags(flags):
env.Replace(FORTRANFLAGS = flags)
env.Replace(F77FLAGS = flags)
env.Replace(F90FLAGS = flags)
env.Replace(F95FLAGS = flags)
mod_dir_prefix = {
"gfortran": "-J ",
"ifort": "-???",
"pgfortran": "-module "
}
flags = {
("gfortran", "debug"): "-O0 -g -Wall -Wextra -pedantic -fimplicit-none -fbounds-check -fbacktrace",
("gfortran", "release"): "-O3",
("pgfortran", "debug"): "-O0 -g -C -traceback",
("pgfortran", "release"): "-O4"
}
if not GetOption('clean'):
print "\nAvailable Fortran compilers:\n"
for k, v in compilers.compilers_dict().iteritems():
print "%10s : %s" % (k, v)
compiler = raw_input("\nChoose compiler: ")
set_compiler(compiler)
debug_or_release = raw_input("\nDebug or release: ")
set_flags(flags[(compiler, debug_or_release)])
env.Replace(FORTRANMODDIRPREFIX = mod_dir_prefix[compiler])
env.Replace(LINK = compiler)
env.Replace(LINKCOM = "$LINK -o $TARGET $LINKFLAGS $SOURCES $_LIBDIRFLAGS $_LIBFLAGS $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS")
env.Replace(LINKFLAGS = "")
env.Replace(FORTRANMODDIR = '#Mod')
Export('env')
SConscript('Sources/SConscript', variant_dir='Build', duplicate=0)
compilers.py is my own module to find some Fortran compilers which are available.
In Sources folder we have a couple of Fortran source files.
Sources\SConscript
Import('env')
env.Program('app', Glob('*.f90'))
Scons supports Fortran and everything works fine.
gfortran -o Temp\kinds.o -c -O3 -JMod Sources\kinds.f90
gfortran -o Temp\math.o -c -O3 -JMod Sources\math.f90
gfortran -o Temp\sorts.o -c -O3 -JMod Sources\sorts.f90
gfortran -o Temp\utils.o -c -O3 -JMod Sources\utils.f90
gfortran -o Temp\main.o -c -O3 -JMod Sources\main.f90
gfortran -o Temp\app.exe Temp\kinds.o Temp\main.o Temp\math.o Temp\sorts.o Temp\utils.o
scons: done building targets.
After renaming variant_dir name to let say #Bin or #Build we get error message:
gfortran -o Bin\kinds.o -c -O3 -JMod Sources\kinds.f90
gfortran -o Bin\main.o -c -O3 -JMod Sources\main.f90
Sources\main.f90:3.11:
USE sorts
1
Fatal Error: Can't open module file 'sorts.mod' for reading at (1): No such file or directory
Of course the order of compilation matters. But why it depends on variant_dir name? Seems like a bug, but maybe I'm doing something wrong.
P.S. This behavior doesn't depend on duplicate variable value.
P.P.S. Tested with SCons 2.0.1 on Windows with Python 2.7 and Mac OS X with Python 2.5.1.
This is a reply to an old thread, but I had virtually the same problem and needed to dig around for a solution.
Firstly, your build order is probably off because the dependency scanner for Fortran does not work properly. Try running
scons [your_arguments] -n --tree=all | less
which won't actually compile anything but show you the commands and in the end will print the dependency tree as Scons sees it.
A possible solution:
Try adding the line (I added your source for context):
env.Replace(FORTRANMODDIR = '#Mod')
env.Replace(FORTRANPATH = '.' ]
Export('env')
As far as I understand, paths are relative to the "virtual" location of the SConscript file (i.e. the src directory or the variant build directory), this should add the directory containing the source files to the scanner's search path.
In my version of scons (2.3.0), I cannot use the duplicate=0 argument, since it automatically inserts the original source directory into the module path, causing the command line to look like -module build/ -module src/ (ifort) and essentially overriding my preference not to clutter the source directory. This might be a bug, though.