How can I build a program using g++ with SCons, without depending on any external environmental variables? - g++

I am trying to build a simple c++ hello world program using g++ with SCons. How can I specify that I want SCons to use g++ without any dependencies on external environment variables, such as PATH?
This is what I've tried:
env = Environment(CXX = 'C:/cygwin/bin/g++')
env.Program('helloworld.c++')
This is my result:
scons: warning: No version of Visual Studio compiler found - C/C++
compilers most likely not set correctly
File "C:\Python27\Scripts\scons.py", line 201, in <module>
C:/cygwin/bin/g++ /Fohelloworld.obj /c helloworld.c++ /TP /nologo
g++: error: /Fohelloworld.obj: No such file or directory
g++: error: /c: No such file or directory
g++: error: /TP: No such file or directory
g++: error: /nologo: No such file or directory
scons: *** [helloworld.obj] Error 1
scons: building terminated because of errors.

Turns out the answer was staring me straight in the face. My first problem was that the first part of this answer:
import os
env = Environment(ENV = {'PATH' : os.environ['PATH']})
utilizes the OS PATH, which I explicitly wanted to avoid. My second problem was that I completely overlooked the answer below, which was the precise answer to my question:
The way to guarantee that the build is repeatable is to explicitly
initialize the PATH
path= ['/bin', '/usr/bin', '/path/to/other/compiler/bin']
env = Environment(ENV = {'PATH' : path})
The reason I didn't realize this was the solution is because I simply misunderstood that env['ENV']['PATH'] and os.environ['PATH'] are completely separate and distinct. Whereas os.environ['PATH'] is obviously the external OS PATH, env['ENV']['PATH'] seems to be SCons' internal equivalent. You can set is to be whatever you please.
In the end, the precise solution I chose, and the one most readable to me was:
PATH = {'PATH' : ['C:/cygwin/bin']}
env = Environment(ENV = PATH)
env['ENV'] = PATH
env['CXX'] = 'g++'
env.Program('helloworld.c++')

A slightly cleaner way
env = Environment(tools=['g++','gnulink'])
env['ENV']['PATH'] = ['C:/cygwin/bin']
env['CXX'] = 'g++'
env.Program('helloworld.c++')
Another way:
env = Environment(tools=['g++','gnulink'])
env.PrependENVPath('PATH','C:/cygwin/bin')
env.Program('helloworld.c++')

Try this:
env = Environment(tools=['ar', 'cc', 'clang', 'clangxx', 'gcc', 'g++', 'gnulink', 'link'], ENV=os.environ, toolpath=['custom_path']).
The command above will:
Create a variable env of type Environment.
Tells scons to set up requires tools.
Find these tools in system variable.
Else find in custom path(you can omit the last parameter: toolpath=['custom_path'] if you don't need it).
Where:
ar: Sets construction variables for the ar library archiver.
Sets: $AR, $ARCOM, $ARFLAGS, $LIBPREFIX, $LIBSUFFIX, $RANLIB, $RANLIBCOM, $RANLIBFLAGS.
cc: Sets construction variables for generic POSIX C compilers.
Sets: $CC, $CCCOM, $CCFLAGS, $CFILESUFFIX, $CFLAGS, $CPPDEFPREFIX, $CPPDEFSUFFIX,$FRAMEWORKPATH, $FRAMEWORKS, $INCPREFIX, $INCSUFFIX, $SHCC, $SHCCCOM, $SHCCFLAGS,$SHCFLAGS, $SHOBJSUFFIX.
clang: Set construction variables for the Clang C compiler.
Sets: $CC, $CCVERSION, $SHCCFLAGS.
clangxx: Set construction variables for the Clang C++ compiler.
Sets: $CXX, $CXXVERSION, $SHCXXFLAGS, $SHOBJSUFFIX,$STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME.
g++: Set construction variables for the gXX C++ compiler.
Sets: $CXX, $CXXVERSION, $SHCXXFLAGS, $SHOBJSUFFIX.
gcc: Set construction variables for the gcc C compiler.
Sets: $CC, $CCVERSION, $SHCCFLAGS.
gnulink: Set construction variables for GNU linker/loader
Sets: $LDMODULEVERSIONFLAGS, $RPATHPREFIX, $RPATHSUFFIX, $SHLIBVERSIONFLAGS,$SHLINKFLAGS, $_LDMODULESONAME, $_SHLIBSONAME.
link: Sets construction variables for generic POSIX linkers. This is a "smart" linker tool which selects a compiler tocomplete the linking based on the types of source files.
Sets: $LDMODULE, $LDMODULECOM, $LDMODULEFLAGS, $LDMODULENOVERSIONSYMLINKS,$LDMODULEPREFIX, $LDMODULESUFFIX, $LDMODULEVERSION, $LDMODULEVERSIONFLAGS,$LIBDIRPREFIX, $LIBDIRSUFFIX, $LIBLINKPREFIX, $LIBLINKSUFFIX, $LINK,$LINKCOM, $LINKFLAGS, $SHLIBSUFFIX, $SHLINK, $SHLINKCOM, $SHLINKFLAGS,$__LDMODULEVERSIONFLAGS, $__SHLIBVERSIONFLAGS.Uses: $LDMODULECOMSTR, $LINKCOMSTR, $SHLINKCOMSTR
Or you can use default config tools for your system by:
env = Environment(tools=['default'], ENV=os.environ)
To quote from scons man 4.1.0
default
Sets construction variables for a default list of Tool modules. Use
default in the tools list to retain the original defaults, since the
tools parameter is treated as a literal statement of the
tools to be made available in that construction environment, not
an addition.
The list of tools selected by default is not static, but
is dependent both on the platform and on the software installed on the
platform. Some tools will not initialize if an underlying command is
not found, and some tools are selected from a list of choices on
a first-found basis. The finished tool list can be examined
by inspecting the TOOLS construction variable in the construction
environment.
On all platforms, all tools from the following list are
selected whose respective conditions are met: filesystem,wix, lex,
yacc, rpcgen, swig, jar, javac, javah, rmic, dvipdf, dvips, gs, tex,
latex, pdflatex, pdftex, tar, zip, textfile.
On Linux systems, the
default tools list selects (first-found): a C compiler from gcc,
intelc, icc, cc; a C++ compilerfrom g++, intelc, icc, cxx; an
assembler from gas, nasm, masm; a linker from gnulink, ilink; a
Fortran compilerfrom gfortran, g77, ifort, ifl, f95, f90, f77; and a
static archiver 'ar'. It also selects all found from the list m4,
rpm.
On Windows systems, the default tools list selects
(first-found): a C compiler from msvc, mingw, gcc, intelc,icl,
icc, cc, bcc32; a C++ compiler from msvc, intelc, icc, g++, cxx,
bcc32; an assembler from masm, nasm, gas,386asm; a linker from mslink,
gnulink, ilink, linkloc, ilink32; a Fortran compiler from gfortran,
g77, ifl, cvf, f95,f90, fortran; and a static archiver from mslib, ar,
tlib; It also selects all found from the list msvs, midl.
On MacOS
systems, the default tools list selects (first-found): a C compiler
from gcc, cc; a C++ compiler from g++, cxx; an assembler 'as'; a
linker from applelink, gnulink; a Fortran compiler from gfortran, f95,
f90, g77; anda static archiver ar. It also selects all found from the
list m4, rpm.
Default lists for other platforms can be found by
examining the scons source code (see SCons/Tool/init.py).

Related

How can I define an executable that has different compiler command-line arguments?

I have a C++ project and I want to test the compatibility of library headers with different compiler versions. I have a simple source file (that includes said headers) and I want to change the compiler argument to std=gnu++11 for this one target. How do I do that?
executable('old_compiler_test', ['octest.cxx']
# override ARGS here ??? how
)
Note that I have
add_global_arguments(
['-std=gnu++17',
....
rather than the dedicated option for this, in spite of the warning to prefer the special option, because the special option simply doesn't work. (Why is a question I've never tracked down)
update
To clarify: I'm not trying to make additional configurations in the same way that debug and release are configurations. I want a different compiler argument to be applied to a single target within the configuration.
From the Meson documentation, you can use the argument <languagename>_args to pass additional compiler arguments. In your case, since you use C++, it would give something like
executable('old_compiler_test', ['octest.cxx'],
cpp_args: ['std=gnu++11']
)
However the documentation also specify that there are no way to disable an argument added by add_global_argument(), so you will end up with both -std=gnu++17 and -std=gnu++11 passed to the compiler. I don't know how your compiler will behave, but I tried to pass both arguments to GCC 10.2 and it uses c++17 (not what you want).
Workaround
It seems that if you define the C++ version in the project() statement, Meson will removes it if an other version is specified in compiler arguments, giving the behaviour you expect.
Here is the sample I used:
meson.build
project('project-name', 'cpp',
default_options: ['cpp_std=c++17']
)
executable('old_compiler_test', ['octest.cxx'],
cpp_args: ['-std=gnu++11']
)
octest.cxx
#include <iostream>
int main() {
std::cout << __cplusplus << std::endl;
}
After compilation, running the executable will print 201103, which means that the compiler used c++11 as desired.

Successful build of Kicad 4.0.6 in Linux Mageia 5 via fixing a wx-3.0 symbol

I have managed to build the Kicad 4.0.6 in Linux Mageia 5.1 with gcc version 4.9.2. I first manually fixed two wxWidgets 3.0.2 header files in the /usr/include/wx-3.0/wx/ directory: regex.h and features.h. Kicad then compiled successfully. With the native wx-3.0 headers, the compiler generated the error in pcbnew/netlist_reader.cpp due to the undefined variable wxRE_ADVANCED.
The features.h header checks if the macro WX_NO_REGEX_ADVANCED is defined. If yes, features.h UNdefines wxHAS_REGEX_ADVANCED macro, and defines it, if no. The macro wxHAS_REGEX_ADVANCED, in turn, is used in regex.h to determine if among the enum constants wxRE_ADVANCED = 1 is present. The standard prebuilt Mageia 5 packages wxgtku3.0_0 and lib64wxgtku3.0-devel that I installed with the use of Mageia's software manager urpmi from Mageia repository WX_NO_REGEX_ADVANCED is defined, therefore wxHAS_REGEX_ADVANCED is undefined, and, hence, wxRE_ADVANCED is undefined either. Kicad 4.0.6 source package assumes wxRE_ADVANCED = 1, therefore the build process stops with the error.
Then I reverted /usr/include/wx-3.0/wx/regex.h and features.h to their original state and learned how to add the definition of wxRE_ADVANCED to CMakeLists.txt. However, I still have a question.
The recommended format of adding the definition to CMakeLists.txt I found at CMake command line for C++ #define is this:
if (NOT DEFINED wxRE_ADVANCED)
set(wxRE_ADVANCED 1)
endif()
add_definitions(-DwxRE_ADVANCED=$(wxRE_ADVANCED))
However, it did not work! The macro expansion for wxRE_ADVANCED in pcbnew/netlist_reader.cpp was empty. I printed it at compile time inserting the following lines into the netlist_reader.cpp file (this was hard to find, most of the recommended formats did not work. The correct one is in C preprocessor: expand macro in a #warning):
#define __STRINGIFY(TEXT) #TEXT
#define __WARNING(TEXT) __STRINGIFY(GCC warning TEXT)
#define WARNING(VALUE) __WARNING(__STRINGIFY(wxRE_ADVANCED = VALUE))
Pragma (WARNING(wxRE_ADVANCED))
Finally, I simplified the CMakeLists.txt definition down to this, and it was a success:
if (NOT DEFINED wxRE_ADVANCED)
set(wxRE_ADVANCED 1)
endif()
add_definitions(-DwxRE_ADVANCED=1)
My question: what is the meaning of "-DwxRE_ADVANCED=$(wxRE_ADVANCED)" if it does not work? Is it possible not to use set(wxRE_ADVANCED 1), and simply write add_definitions(-DwxRE_ADVANCED=1)? Thank you.
P.S. Yes, the Kicad 4.0.6 build process successfully finished with only one line added to the top level CMakeLists.txt file:
add_definitions(-DwxRE_ADVANCED=1)
A variable is called via $variable or ${variable}. Note the curly brackets, not parentheses.
Also, it is recommended to use:
target_compile_definitions(mytarget PUBLIC wxRE_ADVANCED=1)
on a target directly, rather than the general add_definitions() command.

Linking libblas.dll with g77.exe

I downloaded libblas.dll win32 version, "Prebuilt dynamic libraries using Mingw" from https://icl.cs.utk.edu/lapack-for-windows/lapack/#libraries and used a g77 blas sample "blas3_d_prb.f" from http://people.sc.fsu.edu/~jburkardt/f77_src/blas3_d/blas3_d.html with my g77 compiler, I already tried by converting "libblas.lib" to "libblas.a" with reimp and pexports etc.... but unsuccessful.
I hope anybody have some experience with using libblas.dll with g77(because linking the dll with g77 is seems to be tricky), I also want to confirm calling convention used by "libblas.dll" std or cdecl(what g77 follows)?
Thanks.
I finally determined the problems compiling this particular fortran blas program:
Actuallly you need sources (for blas0.f and blas3_d.f) not the libblas.dll(Since it is unknown which sources they used)
Blas0.f also required for auxiliary functions used e.g. r8mat_test, r8mat_print etc.
Compile each library i.e. blas0.f and blas3_d.f to object file with this command:
g77 -c blas0.f
g77 -c blas3_d.f
This will produce blas0.o, and blas3_d.o object files then you will compile main prog like this: (PS: Replace trime functs in blas3_d_prf.f with len_trim)
G77.EXE blas3_d_prf.f blas0.o blas3_d.o -o yourblas.exe
It will generate yourblas.exe binary for windows.

Get full C++ compiler command line

In CMake, the flags for the C++ compiler can be influenced in various ways: setting CMAKE_CXX_FLAGS manually, using add_definitions(), forcing a certain C++ standard, and so forth.
In order to compile a target in the same project with different rules (a precompiled header, in my case), I need to reproduce the exact command that is used to compile files added by a command like add_executable() in this directory.
Reading CMAKE_CXX_FLAGS only returns the value set to it explicitly, CMAKE_CXX_FLAGS_DEBUG and siblings only list default Debug/Release options. There is a special functions to retrieve the flags from add_definitions() and add_compiler_options(), but none seem to be able to return the final command line.
How can I get all flags passed to the compiler into a CMake variable?
To answer my own question: It seems like the only way of getting all compiler flags is to reconstruct them from the various sources. The code I'm working with now is the following (for GCC):
macro (GET_COMPILER_FLAGS TARGET VAR)
if (CMAKE_COMPILER_IS_GNUCXX)
set(COMPILER_FLAGS "")
# Get flags form add_definitions, re-escape quotes
get_target_property(TARGET_DEFS ${TARGET} COMPILE_DEFINITIONS)
get_directory_property(DIRECTORY_DEFS COMPILE_DEFINITIONS)
foreach (DEF ${TARGET_DEFS} ${DIRECTORY_DEFS})
if (DEF)
string(REPLACE "\"" "\\\"" DEF "${DEF}")
list(APPEND COMPILER_FLAGS "-D${DEF}")
endif ()
endforeach ()
# Get flags form include_directories()
get_target_property(TARGET_INCLUDEDIRS ${TARGET} INCLUDE_DIRECTORIES)
foreach (DIR ${TARGET_INCLUDEDIRS})
if (DIR)
list(APPEND COMPILER_FLAGS "-I${DIR}")
endif ()
endforeach ()
# Get build-type specific flags
string(TOUPPER ${CMAKE_BUILD_TYPE} BUILD_TYPE_SUFFIX)
separate_arguments(GLOBAL_FLAGS UNIX_COMMAND
"${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${BUILD_TYPE_SUFFIX}}")
list(APPEND COMPILER_FLAGS ${GLOBAL_FLAGS})
# Add -std= flag if appropriate
get_target_property(STANDARD ${TARGET} CXX_STANDARD)
if ((NOT "${STANDARD}" STREQUAL NOTFOUND) AND (NOT "${STANDARD}" STREQUAL ""))
list(APPEND COMPILER_FLAGS "-std=gnu++${STANDARD}")
endif ()
endif ()
set(${VAR} "${COMPILER_FLAGS}")
endmacro ()
This could be extended to also include options induced by add_compiler_options() and more.
Easiest way is to use make VERBOSE=1 when compiling.
cd my-build-dir
cmake path-to-my-sources
make VERBOSE=1
This will do a single-threaded build, and make will print every shell command it runs just before it runs it. So you'll see output like:
[ 0%] Building CXX object Whatever.cpp.o
<huge scary build command it used to build Whatever.cpp>
There actually is a fairly clean way to do this at compile time using CXX_COMPILER_LAUNCHER:
If you have a script print_args.py
#!/usr/bin/env python
import sys
import argparse
print(" ".join(sys.argv[1:]))
# we need to produce an output file so that the link step does not fail
p = argparse.ArgumentParser()
p.add_argument("-o")
args, _ = p.parse_known_args()
with open(args.o, "w") as f:
f.write("")
You can set the target's properties as follows:
add_library(${TARGET_NAME} ${SOURCES})
set_target_properties(${TARGET_NAME} PROPERTIES
CXX_COMPILER_LAUNCHER
${CMAKE_CURRENT_SOURCE_DIR}/print_args.py
)
# this tells the linker to not actually link. Which would fail because output file is empty
set_target_properties(${TARGET_NAME} PROPERTIES
LINK_FLAGS
-E
)
This will print the exact compilation command at compile time.
Short answer
It's not possible to assign final value of compiler command line to variable in CMake script, working in all use cases.
Long answer
Unfortunately, even solution accepted as answer still not gets all compiler flags. As gets noted in comments, there are Transitive Usage Requirements. It's a modern and proper way to write CMake files, getting more and more popular. Also you may have some compile options defined using generator expressions (they look like variable references but will not expand when needed).
Consider having following example:
add_executable(myexe ...);
target_compile_definitions(myexe PRIVATE "PLATFORM_$<PLATFORM_ID>");
add_library(mylib ...);
target_compile_definitions(mylib INTERFACE USING_MY_LIB);
target_link_libraries(myexe PUBLIC mylib);
If you try to call proposed GET_COMPILER_FLAGS macro with myexe target, you will get resulting output -DPLATFORM_$<PLATFORM_ID> instead of expected -DPLATFORM_Linux -DUSING_MY_LIB.
This is because there are two stages between invoking CMake and getting build system generated:
Processing. At this stage CMake reads and executes commands from cmake script(s), particularly, variable values getting evaluated and assigned. At this moment CMake just collecting all required info and being prepared to generate build system (makefiles).
Generating. CMake uses values of special variables and properties, being left at end of processed scripts to finally decide and form generated output. This is where it constructs final command line for compiler according to its internal algorithm, not avaliable for scripting.
Target properties which might be retrieved at processing stage with get_target_property(...) or get_property(... TARGET ...) aren't complete (even when invoked at the end of script). At generating stage CMake walks through each target dependency tree (recursively) and appends properties values according to transitive usage requirements (PUBLIC and INTERFACE tagged values gets propagated).
Although, there are workarounds, depending on what final result you aiming to achieve. This is possible by applying generator expressions, which allows use final values of properties of any target (defined at processing stage)... but later!
Two general possibilites are avaliable:
Generate any output file based on template, which content contains variable references and/or generator expressions, and defined as either string variable value, or input file. It's not flexible due to very limited support of conditional logic (i.e. you cannot use complex concatenations available only with nested foreach() loops), but has advantages, that no further actions required and content described in platform-independent way. Use file(GENERATE ...) command variant. Note, that it behaves differently from file (WRITE ...) variant.
Add custom target (and/or custom command) which implements further usage of expanded value. It's platform dependent and requires user to additionally invoke make (either with some special target, or include to all target), but has advantage, that it's flexible enough because you may implement shell script (but without executable bit).
Example demonstrating solution with combining these options:
set(target_name "myexe")
file(GENERATE OUTPUT script.sh CONTENT "#!/bin/sh\n echo \"${target_name} compile definitions: $<TARGET_PROPERTY:${target_name},COMPILE_DEFINITIONS>\"")
add_custom_target(mycustomtarget
COMMAND echo "\"Platform: $<PLATFORM_ID>\""
COMMAND /bin/sh -s < script.sh
)
After calling CMake build directory will contain file script.sh and invoking make mycustomtarget will print to console:
Platform: Linux
myexe compile definitions: PLATFORM_Linux USING_MY_LIB
Use
set(CMAKE_EXPORT_COMPILE_COMMANDS true)
and get compile_commands.json

Does Xcode's objective-c compiler optimize for bit shifts?

Does the objective-c compiler in Xcode know better, or is it faster if I use bit shift for multiplications and divisions by powers of 2?
NSInteger parentIndex = index >> 1; // integer division by 2
Isn't this a bit 1980's? Don't processors run these instructions in the same time these days? I remember back in my 68000 days when a div was 100+ cycles and a shift only 3 or 4... not sure this is the case any more as processors have moved on.
Why don't you get the compiler to generate the assembler file and have a look what it's generating and run some benchmarks.
I found this on the web which may help you... although it's for 'C' I think most of the options will be the same.
Q: How can I peek at the assembly code generated by GCC?
Q: How can I create a file where I can see the C code and its assembly
translation together?
A: Use the -S (note: capital S) switch to GCC, and it will emit the assembly code to a file with a .s extension. For example, the following command:
gcc -O2 -S -c foo.c
will leave the generated assembly code on the file foo.s.
If you want to see the C code together with the assembly it was converted to, use a command line like this:
gcc -c -g -Wa,-a,-ad [other GCC options] foo.c > foo.lst
which will output the combined C/assembly listing to the file foo.lst.
If you need to both get the assembly code and to compile/link the program, you can either give the -save-temps option to GCC (which will leave all the temporary files including the .s file in the current directory), or use the -Wa,aln=foo.s option which instructs the assembler to output the assembly translation of the C code (together with the hex machine code and some additional info) to the file named after the =.