How to pass customized arguments from Makefile to Rust codes in Cargo? - rust

Now I have a project managed by Makefile. That is, make run is the entry for the project. Rust codes, managed by Cargo, is a part of my project.
What I want to achieve is to pass some arguments from Makefile to Rust codes.
I know how to achieve this in C:
$ gcc --help | grep -- -D
-D <macro>=<value> Define <macro> to <value> (or 1 if <value> omitted)
So, I can just pass arguments from make run MYARGUMENT=xxx and in Makefile pass $(MYARGUMENT) to gcc.
How to achieve it if I want to pass MYARGUMENT to command cargo run?
Or, I kind of want a feature like this -- to disable some statements in Rust codes, which could be controlled from Makefile:
#ifdef XXX
printf("XXX is define!\n");
#endif

Using gcc, you can use -D to specify a predefined macro and then use preprofessor directives to compile some code conditionally as below:
int main(void)
{
#if defined(DEBUG_)
printf("Debug mode\n");
#endif
}
And pass -D to tell gcc a predefined macro: gcc -DDEBUG.
Using rustc, you can use cfg equivalently:
fn main() {
#[cfg(DEBUG)]
{
println!("Debug mode");
}
}
Then run rustc --cfg 'DEBUG' and you get the same result as gcc.
If you want to use cargo instead of use rustc directly, you can see this question: How do I use conditional compilation with cfg and Cargo?

Related

conditional compilation in makefile so that command-line arguments are ignored

Using gcc compiler on linux, I have a C program that used command-line argument (one argument) like
./myprog 0
I want to write a makefile that uses conditional compilation so that if I compile like
make SPECIAL=1
then command-line argument is used.
if I compile without SPECIAL like
make
then command-line argument is ignored even if we enter it.
How to make it possible.
I am using following compilation command
gcc -o myprog myprog.c prog2.c prog3.c
The makefile can be trivial but the real work has to happen in the C code. Something like this, as a minimal example:
#include <stdio.h>
int main(int argc, char **argv, char **envp) {
int i;
#ifndef SPECIAL
argc=1;
argv[1] = NULL;
#endif
for(i=1; i<argc; ++i) {
printf(">>%s<<\n", argv[i]);
}
}
Now, you don't even really need a Makefile for this simple program.
bash$ make CFLAGS=-DSPECIAL=1 -f /dev/null myprog
cc -DSPECIAL=1 myprog.c -o myprog
Having said that, making your build nondeterministic by introducing variations which depend on ephemeral build-time whims is a recipe for insanity. Have two separate targets which create two separate binaries, one with the regular behavior, and the other with the foot-gun semantics.
DEPS := myprog.c prog2.c prog3.c # or whatever your dependencies are
myprog: $(DEPS)
myprog-footgun: CLAGS+=-DSPECIAL=1
myprog-footgun: $(DEPS) # same dependencies, different output file
$(CC) $(CFLAGS) -o $# $^
See Target-specific Variable Values in the GNU Make documentation for details of this syntax.
(Notice that Stack Overflow renders tabs in the Markdown source as spaces, so you will not be able to copy/paste this verbatim.)
I would perhaps in fact reverse the meaning of SPECIAL so that it enables the foot-gun version, rather than the other way around (the original version of this answer had this design, just because I had read your question that way originally).

How to set custom predefined macros for gcc via Linux environment to avoid passing them all the time

I have several projects in which I use many custom macros. For example,
//prog.c
#include <stdio.h>
#ifdef DBG_LEVEL_1
#define P(d) printf("%s:%d\n", #d, d);
#else
#define P(...)
#endif
#ifdef INLINE
#define INL static inline
#else
#define INL
#endif
INL void func_Sqr(int a)
{
printf("sqr(a):%d\n", a*a);
}
int main()
{
int a = 3;
P(a);
func_Sqr(a);
return 0;
}
I compile this in several ways,
gcc prog.c
gcc -DINLINE prog.c
gcc -DDBG_LEVEL_1 prog.c
gcc -DDBG_LEVEL_1 -DINLINE-DINLINE prog.c
Is there way to set these macros as enabled as default via environment variable?
We can solve this by creating a makefile where these macros are set. But I want to know if there any Linux environment related solution
Is there way to set these macros as enabled as default via environment variable?
Generally, no there is not. Gcc arguments are not affected by environment variables (except maybe DEPENDENCIES_OUTPUT..).
I advise to write a small wrapper function around the command, for example a bash function:
wgcc() {
gcc "${GCCARGS[#]}" "$#"
}
and then doing:
GCCARGS+=(-DA=1 -DB=2)
wgcc something.c
is trivial to do, easy to understand and maintain and communicate with other team members, easy to implement and share. Suprising haisenbugs will be easy to track - wgcc is unique name different then gcc. Still you could overwrite the original command with gcc() { command gcc "${GCCARGS[#]}" "$#"; } or by creating a /usr/local/bin/gcc file, making it a bit more confusing.
But! You can and I woult strongly advise not to do that, because it will be confusing to others and hard to maintain. You can use COMPILER_PATH environment variable to overwrite compiler tools and provide custom options. In steps:
Create a temporary directory
In that directory link all the subprograms of gcc to it's normal prefix, except the tools the behavior you want to modify, like cc1.
Then create cc1 as a script with that will call the original cc1 but will use some environment variable to pass extra arguments.
Then export the path as COMPILER_PATH so gcc will pick it up.
On my archlinux with gcc10.0.1 I did:
mkdir /tmp/temp
cd /tmp/temp
for i in /usr/lib/gcc/x86_64-pc-linux-gnu/10.1.0/{cc1plus,collect2,lto-wrapper,lto1}; do ln -vs "$i"; done
printf "%s\n" '#!/bin/sh' '/usr/lib/gcc/x86_64-pc-linux-gnu/10.1.0/cc1 $GCCARGS "$#"' > cc1
chmod +x cc1
export COMPILER_PATH=/tmp/temp
After that you can:
export GCCARGS=-DA=1
gcc -xc - <<<'int main() { printf("A=%d\n", A); }'
./a.out
> A=1

How to pass arbitrary compiler CFLAGS with Scons from the command line without custom code?

Is there a way to write something like:
scons CFLAGS='-fsanitize=address -fsanitize=thread'
which would just work with a minimal script:
env = Environment()
env.Program(target='main.out', source=['main.c'])
without changing that script?
I know how to do it by modifying the script with AddOption + env.Append(CCFLAGS but I'm wondering it it is possible without changing the code to explicitly support it.
I ended up going with:
env = Environment()
env.Append(CCFLAGS='-Werror')
env.Append(CCFLAGS=ARGUMENTS.get('CCFLAGS', ''))
env.Program(target='main.out', source=['main.c'])
which can be used as:
scons CCFLAGS='-Wall -pedantic'
and will compile as:
gcc -o main.o -c -Werror -Wall -pedantic main.c
You likely want to keep the env.Append(CCFLAGS=ARGUMENTS.get('CCFLAGS', '')) line as the very last change made to CCFLAGS, since this will allow overriding the defaults on the command line: GCC tends to just use the last value seen as the actual one.
TODO: how to make it work with Variables? This would be nicer since we can get more error checking and the help message generation:
variables = Variables(None, ARGUMENTS)
variables.Add('CCFLAGS', 'my help', '')
env = Environment(variables)
env.Append(CCFLAGS='$CCFLAGS')
env.Append(CCFLAGS=['-Werror'])
env.Program(
source=['main.c'],
target='main.out',
)
Help(variables.GenerateHelpText(env))
but this fails due to bad quoting:
gcc -o main.o -c "-Wall -pedantic" -Werror main.c
This is not possible by design (without explicitly changing the build scripts). From the answer to #1 of the most-frequently-asked questions in our FAQ:
SCons does not automatically propagate the external environment used
to execute 'scons' to the commands used to build target files. This is
so that builds will be guaranteed repeatable regardless of the
environment variables set at the time scons is invoked. This also
means that if the compiler or other commands that you want to use to
build your target files are not in standard system locations, SCons
will not find them unless you explicitly set the PATH to include those
locations.

What does the g++ -D flag do?

I am looking at a CFLAGS of -
CFLAGS=-g -w -D LINUX -O3 -fpermissive
in a Makefile. What does the -D flag do? I see on the man page that
-D name
Predefine name as a macro, with definition 1.
but I don't know how to interpret that. My interpretation is...its making LINUX a macro and only doing -03 and -fpermissive when in a linux environment. Is that right? If not, then what? Thanks for any help
It is equivalent to adding the statement #define LINUX 1 in the source code of the file that is being compiled. It does not have any effect on other compilation flags. The reason for this is it's an easy way to enable #ifdef statements in the code. So you can have code that says:
#ifdef LINUX
foo;
#endif
It will only be enabled if that macro is enabled which you can control with the -D flag. So it is an easy way to enable/disable conditional compilation statements at compile time without editing the source file.
It doesn't have anything to do with -O3. Basically, it means the same as
#define LINUX 1
at the beginning of the compiled file.

Howto: Conditional include of header file

I have test.c in which I would like to have the statement
#include "abc.h" (the header file of libabc)
only if test.c has been compiled with libabc as:
gcc test.c -labc
If test.c is simply compiled as
gcc test.c , abc.h should not be included.
How can I do that?
One approach is to use -D to define a macro:
gcc test.c -labc -DABC
#ifdef ABC
#include "abc.h"
#endif
If you're running this command from Bash, then you could in principle create a shell-function wrapper around gcc to automatically add -DABC when -labc is specified:
function gcc ()
{
local arg
for arg in "$#" ; do
if [[ $arg = -labc ]] ; then
command gcc "$#" -DABC
return
fi
done
command gcc "$#"
}
. . . but I don't really recommend that.
Hardly, as the link phase takes place after the compilation is done. You could tweak a Makefile to define a preprocessor macro, and use it as a test condition. I can’t think of any other way.

Resources