How to store a version number in a static library? - c

How can I store a version number in a static library (file.a) and later check for its version in Linux?
P.S. I need possibility to check version of file any time without any special executable using only by shell utilities.

In addition to providing a static string as mentioned by Puppe, it is common practice to provide a macro to retrieve the version check for compatibility. For example, you could have the following macros (declared in a header file to be used with your library):
#define MYLIB_MAJOR_VERSION 1
#define MYLIB_MINOR_VERSION 2
#define MYLIB_REVISION 3
#define MYLIB_VERSION "1.2.3"
#define MYLIB_VERSION_CHECK(maj, min) ((maj==MYLIB_MAJOR_VERSION) && (min<=MYLIB_MINOR_VERSION))
Notice with the MYLIB_CHECK_VERSION macro, I'm assuming you want a specific major rev and a minor rev greater than or equal to your desired version. Change as required for your application.
Then use it from a calling application, something like:
if (! MYLIB_VERSION_CHECK(1, 2)) {
fprintf(stderr, "ERROR: incompatible library version\n");
exit(-1);
}
This approach will cause the version information to come from the included header file. Additionally, it will be optimized at compile time for the calling application. With a little more work, you can extract it from the library itself. Read on...
You can also use this information to create a static string stored inside your library, as mentioned by Puppe. Place something like this inside your library:
struct {
const char* string;
const unsigned major;
const unsigned minor;
const unsigned revision;
} mylib_version = {
MYLIB_VERSION, MYLIB_MAJOR_VERSION, MYLIB_MINOR_VERSION, MYLIB_REVISION
};
This will create a struct called mylib_version in your library. You can use this to do further verifications by creating functions inside your library and accessing those from a calling application, etc.

Maybe you could create a string with the version like this:
char* library_version = { "Version: 1.3.6" };
and to be able to check it from the shell just use:
strings library.a | grep Version | cut -d " " -f 2

Creating a new answer based on your edit... Just to avoid confusion :)
If you are looking for a non-code way to solve the problem, you could try this. It's (yet again) an alternative to the strings approach defined by Puppe.
Maybe you could just touch a file called version_1.2.3 and add it to the archive. Then, you could determine the version by looking for the version file using the ar command:
ar t libmylib.a | grep 'version_' | sed -e 's/^version_//'
I'm not sure if that will get you what you need, but there is no standard method for embedding metadata like this in an archive. Maybe you'll find other information you want to store in this "metafile" for the archive.

If you are using gcc, you can use the #ident directive
#ident "Foo Version 1.2.3.4"
void foo(void){ /* foo code here */ }
To get the version just use one of the following:
strings -a foo.o | grep "Foo Version"
strings -a foo.a | grep "Foo Version"
strings -a foo.so | grep "Foo Version"
This will allow you to compile the version into the library with the capability of later stripping it out using strip -R .comment your_file or completely omit it by passing -fno-ident (This will also omit the compiler version comments from the compiled objects)

Several times man 1 ident has been mentioned, so here are details about using that method.
ident is a command that comes with the RCS (Revision Control System), but might also be available if you are using CVS (Concurrent Versions System), or Subversion.
You would use it like this (cloned from the man page):
#include <stdio.h>
static char const rcsid[] =
"$Id: f.c,v 5.4 1993/11/09 17:40:15 eggert Exp $";
int main() { return printf("%s\n", rcsid) == EOF; }
and f.c is compiled into f.o, then the command
ident f.c f.o
will output
f.c:
$Id: f.c,v 5.4 1993/11/09 17:40:15 eggert Exp $
f.o:
$Id: f.c,v 5.4 1993/11/09 17:40:15 eggert Exp $
If your f.o were added to a static library f.a then ident f.a should show a similar output. If you have several similarly built [a-z].o in your az.a you should find all their strings in the az.a file.
CAVEAT: Just because they are in the .a file doesn't mean they will be included in your program file. Unless the program references them the linker sees no need to include them. So you usually have to have a method in each module to return the string, and the app needs to call that method. There are ways to convince most linkers that it is a required symbol without actually referencing it, but it depends on the linker, and is beyond the scope of this answer.
If instead you are familiar with the SCCS (Source Code Control System) then you would use man 1 what instead, and it would look like this (done with macros to show the flexibility available):
#include <stdio.h>
#define VERSION_STR "5.4"
#define CONFIG "EXP"
#define AUTHOR "eggert"
static char const sccsid[] =
"#(#) " CONFIG " v " VERSION_STR " " __DATE__ " " __TIME__ " " AUTHOR;
int main() { return printf("%s\n", sccsid) == EOF; }
and f.c is compiled into f.o, then the command
what f.c f.o
will output
f.c:
#(#) EXP v 5.4 1993/11/09 17:40:15 eggert
f.o:
#(#) EXP v 5.4 1993/11/09 17:40:15 eggert
PS: both ident and what are commands that come with specific centralized source control systems. If you are using a distributed source control system (like git) the entire concept may not make sense. For some ideas using git see this thread: Moving from CVS to git: $Id:$ equivalent? though the hash isn't the same as a version number. :)

Related

Use preprocessor to "remove" prefix from function in C

I write a framework with lot of function that are named like that :
ICE_ModuleType_FunctionUse()
and everything else have ICE_ prefix (typename, define etc...)
And with preprocessor I would like to remove ICE_ to reduce function name lenght when the user know there is no conflict with other libs.
But the only working way I found was to write every function, type etc... by hand like that :
#define ModuleType_FunctionUse ICE_ModuleType_FunctionUse
Any Idea on how to easly do that ?
You could automatically create a new header file with a name like use_namespace_ICE.h for your clients to use. This file would have the required list of #defines, and can be generated using the utilities nm or dumpbin applied to your library.
For example, if foo.c is:
void ICE_ModuleType_FunctionUse(void) { /* code */ }
then:
cc -c -o foo.o foo.c
nm foo.o | grep ' T _ICE_' | sed 's/.* T _ICE_\(.*\)/#define \1 ICE_\1/'
yields:
#define ModuleType_FunctionUse ICE_ModuleType_FunctionUse
As the comments tell you, there is no way, or no easy way, to shorten identifiers once written in your source code. However, you can reduce the typing for things that still need to be written:
#define ModuleType_FunctionUse ICE_ModuleType_FunctionUse
This defines that the short name will be replaced with the longer name.

How can execute a decrypted file residing in the memory? [duplicate]

Is it possible to compile a C++ (or the like) program without generating the executable file but writing it and executing it directly from memory?
For example with GCC and clang, something that has a similar effect to:
c++ hello.cpp -o hello.x && ./hello.x $# && rm -f hello.x
In the command line.
But without the burden of writing an executable to disk to immediately load/rerun it.
(If possible, the procedure may not use disk space or at least not space in the current directory which might be read-only).
Possible? Not the way you seem to wish. The task has two parts:
1) How to get the binary into memory
When we specify /dev/stdout as output file in Linux we can then pipe into our program x0 that reads
an executable from stdin and executes it:
gcc -pipe YourFiles1.cpp YourFile2.cpp -o/dev/stdout -Wall | ./x0
In x0 we can just read from stdin until reaching the end of the file:
int main(int argc, const char ** argv)
{
const int stdin = 0;
size_t ntotal = 0;
char * buf = 0;
while(true)
{
/* increasing buffer size dynamically since we do not know how many bytes to read */
buf = (char*)realloc(buf, ntotal+4096*sizeof(char));
int nread = read(stdin, buf+ntotal, 4096);
if (nread<0) break;
ntotal += nread;
}
memexec(buf, ntotal, argv);
}
It would also be possible for x0 directly execute the compiler and read the output. This question has been answered here: Redirecting exec output to a buffer or file
Caveat: I just figured out that for some strange reason this does not work when I use pipe | but works when I use the x0 < foo.
Note: If you are willing to modify your compiler or you do JIT like LLVM, clang and other frameworks you could directly generate executable code. However for the rest of this discussion I assume you want to use an existing compiler.
Note: Execution via temporary file
Other programs such as UPX achieve a similar behavior by executing a temporary file, this is easier and more portable than the approach outlined below. On systems where /tmp is mapped to a RAM disk for example typical servers, the temporary file will be memory based anyway.
#include<cstring> // size_t
#include <fcntl.h>
#include <stdio.h> // perror
#include <stdlib.h> // mkostemp
#include <sys/stat.h> // O_WRONLY
#include <unistd.h> // read
int memexec(void * exe, size_t exe_size, const char * argv)
{
/* random temporary file name in /tmp */
char name[15] = "/tmp/fooXXXXXX";
/* creates temporary file, returns writeable file descriptor */
int fd_wr = mkostemp(name, O_WRONLY);
/* makes file executable and readonly */
chmod(name, S_IRUSR | S_IXUSR);
/* creates read-only file descriptor before deleting the file */
int fd_ro = open(name, O_RDONLY);
/* removes file from file system, kernel buffers content in memory until all fd closed */
unlink(name);
/* writes executable to file */
write(fd_wr, exe, exe_size);
/* fexecve will not work as long as there in a open writeable file descriptor */
close(fd_wr);
char *const newenviron[] = { NULL };
/* -fpermissive */
fexecve(fd_ro, argv, newenviron);
perror("failed");
}
Caveat: Error handling is left out for clarities sake. Includes for sake of brevity.
Note: By combining step main() and memexec() into a single function and using splice(2) for copying directly between stdin and fd_wr the program could be significantly optimized.
2) Execution directly from memory
One does not simply load and execute an ELF binary from memory. Some preparation, mostly related to dynamic linking, has to happen. There is a lot of material explaining the various steps of the ELF linking process and studying it makes me believe that theoretically possible. See for example this closely related question on SO however there seems not to exist a working solution.
Update UserModeExec seems to come very close.
Writing a working implementation would be very time consuming, and surely raise some interesting questions in its own right. I like to believe this is by design: for most applications it is strongly undesirable to (accidentially) execute its input data because it allows code injection.
What happens exactly when an ELF is executed? Normally the kernel receives a file name and then creates a process, loads and maps the different sections of the executable into memory, performs a lot of sanity checks and marks it as executable before passing control and a file name back to the run-time linker ld-linux.so (part of libc). The takes care of relocating functions, handling additional libraries, setting up global objects and jumping to the executables entry point. AIU this heavy lifting is done by dl_main() (implemented in libc/elf/rtld.c).
Even fexecve is implemented using a file in /proc and it is this need for a file name that leads us to reimplement parts of this linking process.
Libraries
UserModeExec
libelf -- read, modify, create ELF files
eresi -- play with elfes
OSKit (seems like a dead project though)
Reading
http://www.linuxjournal.com/article/1060?page=0,0 -- introduction
http://wiki.osdev.org/ELF -- good overview
http://s.eresi-project.org/inc/articles/elf-rtld.txt -- more detailed Linux-specific explanation
http://www.codeproject.com/Articles/33340/Code-Injection-into-Running-Linux-Application -- how to get to hello world
http://www.acsu.buffalo.edu/~charngda/elf.html -- nice reference of ELF structure
Loaders and Linkers by John Levine -- deeoer explanation of linking
Related Questions at SO
Linux user-space ELF loader
ELF Dynamic loader symbol lookup ordering
load-time ELF relocation
How do global variables get initialized by the elf loader
So it seems possible, you decide whether is also practical.
Yes, though doing it properly requires designing significant parts of the compiler with this in mind. The LLVM guys have done this, first with a kinda-separate JIT, and later with the MC subproject. I don't think there's a ready-made tool doing it. But in principle, it's just a matter of linking to clang and llvm, passing the source to clang, and passing the IR it creates to MCJIT. Maybe a demo does this (I vaguely recall a basic C interpreter that worked like this, though I think it was based on the legacy JIT).
Edit: Found the demo I recalled. Also, there's cling, which seems to do basically what I described, but better.
Linux can create virtual file systems in RAM using tempfs. For example, I have my tmp directory set up in my file system table like so:
tmpfs /tmp tmpfs nodev,nosuid 0 0
Using this, any files I put in /tmp are stored in my RAM.
Windows doesn't seem to have any "official" way of doing this, but has many third-party options.
Without this "RAM disk" concept, you would likely have to heavily modify a compiler and linker to operate completely in memory.
If you are not specifically tied to C++, you may also consider other JIT based solutions:
in Common Lisp SBCL is able to generate machine code on the fly
you could use TinyCC and its libtcc.a which emits quickly poor (i.e. unoptimized) machine code from C code in memory.
consider also any JITing library, e.g. libjit, GNU Lightning, LLVM, GCCJIT, asmjit
of course emitting C++ code on some tmpfs and compiling it...
But if you want good machine code, you'll need it to be optimized, and that is not fast (so the time to write to a filesystem is negligible).
If you are tied to C++ generated code, you need a good C++ optimizing compiler (e.g. g++ or clang++); they take significant time to compile C++ code to optimized binary, so you should generate to some file foo.cc (perhaps in a RAM file system like some tmpfs, but that would give a minor gain, since most of the time is spent inside g++ or clang++ optimization passes, not reading from disk), then compile that foo.cc to foo.so (using perhaps make, or at least forking g++ -Wall -shared -O2 foo.cc -o foo.so, perhaps with additional libraries). At last have your main program dlopen that generated foo.so. FWIW, MELT was doing exactly that, and on Linux workstation the manydl.c program shows that a process can generate then dlopen(3) many hundred thousands of temporary plugins, each one being obtained by generating a temporary C file and compiling it. For C++ read the C++ dlopen mini HOWTO.
Alternatively, generate a self-contained source program foobar.cc, compile it to an executable foobarbin e.g. with g++ -O2 foobar.cc -o foobarbin and execute with execve that foobarbin executable binary
When generating C++ code, you may want to avoid generating tiny C++ source files (e.g. a dozen lines only; if possible, generate C++ files of a few hundred lines at least; unless lots of template expansion happens thru extensive use of existing C++ containers, where generating a small C++ function combining them makes sense). For instance, try if possible to put several generated C++ functions in the same generated C++ file (but avoid having very big generated C++ functions, e.g. 10KLOC in a single function; they take a lot of time to be compiled by GCC). You could consider, if relevant, to have only one single #include in that generated C++ file, and pre-compile that commonly included header.
Jacques Pitrat's book Artificial Beings, the conscience of a conscious machine (ISBN 9781848211018) explains in details why generating code at runtime is useful (in symbolic artificial intelligence systems like his CAIA system). The RefPerSys project is trying to follow that idea and generate some C++ code (and hopefully, more and more of it) at runtime. Partial evaluation is a relevant concept.
Your software is likely to spend more CPU time in generating C++ code than GCC in compiling it.
tcc compiler "-run" option allows for exactly this, compile into memory, run there and finally discard the compiled stuff. No filesystem space needed. "tcc -run" can be used in shebang to allow for C script, from tcc man page:
#!/usr/local/bin/tcc -run
#include <stdio.h>
int main()
{
printf("Hello World\n");
return 0;
}
C scripts allow for mixed bash/C scripts, with "tcc -run" not needing any temporary space:
#!/bin/bash
echo "foo"
sed -n "/^\/\*\*$/,\$p" $0 | tcc -run -
exit
/**
*/
#include <stdio.h>
int main()
{
printf("bar\n");
return 0;
}
Execution output:
$ ./shtcc2
foo
bar
$
C scripts with gcc are possible as well, but need temporary space like others mentioned to store executable. This script produces same output as the previous one:
#!/bin/bash
exc=/tmp/`basename $0`
if [ $0 -nt $exc ]; then sed -n "/^\/\*\*$/,\$p" $0 | gcc -x c - -o $exc; fi
echo "foo"
$exc
exit
/**
*/
#include <stdio.h>
int main()
{
printf("bar\n");
return 0;
}
C scripts with suffix ".c" are nice, headtail.c was my first ".c" file that needed to be executable:
$ echo -e "1\n2\n3\n4\n5\n6\n7" | ./headtail.c
1
2
3
6
7
$
I like C scripts, because you just have one file, you can easily move around, and changes in bash or C part require no further action, they just work on next execution.
P.S:
The above shown "tcc -run" C script has a problem, C script stdin is not available for executed C code. Reason was that I passed extracted C code via pipe to "tcc -run". New gist run_from_memory_stdin.c does it correctly:
...
echo "foo"
tcc -run <(sed -n "/^\/\*\*$/,\$p" $0) 42
...
"foo" is printed by bash part, "bar 42" from C part (42 is passed argv[⁠1]), and piped script input gets printed from C code then:
$ route -n | ./run_from_memory_stdin.c
foo
bar 42
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.29.58.98 0.0.0.0 UG 306 0 0 wlan1
10.0.0.0 0.0.0.0 255.255.255.0 U 0 0 0 wlan0
169.254.0.0 0.0.0.0 255.255.0.0 U 303 0 0 wlan0
172.29.58.96 0.0.0.0 255.255.255.252 U 306 0 0 wlan1
$
One can easily modify the compiler itself. It sounds hard first but thinking about it, it seams obvious. So modifying the compiler sources directly expose a library and make it a shared library should not take that much of afford (depending on the actual implementation).
Just replace every file access with a solution of a memory mapped file.
It is something I am about to do with compiling something transparently in the background to op codes and execute those from within Java.
-
But thinking about your original question it seams you want to speed up compilation and your edit and run cycle. First of all get a SSD-Disk you get almost memory speed (use a PCI version) and lets say its C we are talking about. C does this linking step resulting in very complex operations that are likely to take more time than reading and writing from / to disk. So just put everything on SSD and live with the lag.
Finally the answer to OP question is yes!
I found memrun repo from guitmz, that demoed running (x86_64) ELF from memory, with golang and assembler. I forked that, and provided C version of memrun, that runs ELF binaries (verified on x86_64 and armv7l), either from standard input, or via first argument process substitution. The repo contains demos and documentation (memrun.c is 47 lines of code only):
https://github.com/Hermann-SW/memrun/tree/master/C#memrun
Here is simplest example, with "-o /dev/fd/1" gcc compiled ELF gets sent to stdout, and piped to memrun, which executes it:
pi#raspberrypi400:~/memrun/C $ gcc info.c -o /dev/fd/1 | ./memrun
My process ID : 20043
argv[0] : ./memrun
no argv[1]
evecve --> /usr/bin/ls -l /proc/20043/fd
total 0
lr-x------ 1 pi pi 64 Sep 18 22:27 0 -> 'pipe:[1601148]'
lrwx------ 1 pi pi 64 Sep 18 22:27 1 -> /dev/pts/4
lrwx------ 1 pi pi 64 Sep 18 22:27 2 -> /dev/pts/4
lr-x------ 1 pi pi 64 Sep 18 22:27 3 -> /proc/20043/fd
pi#raspberrypi400:~/memrun/C $
The reason I was interested in this topic was usage in "C script"s. run_from_memory_stdin.c demonstrates all together:
pi#raspberrypi400:~/memrun/C $ wc memrun.c | ./run_from_memory_stdin.c
foo
bar 42
47 141 1005 memrun.c
pi#raspberrypi400:~/memrun/C $
The C script producing shown output is so small ...
#!/bin/bash
echo "foo"
./memrun <(gcc -o /dev/fd/1 -x c <(sed -n "/^\/\*\*$/,\$p" $0)) 42
exit
/**
*/
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("bar %s\n", argc>1 ? argv[1] : "(undef)");
for(int c=getchar(); EOF!=c; c=getchar()) { putchar(c); }
return 0;
}
P.S:
I added tcc's "-run" option to gcc and g++, for details see:
https://github.com/Hermann-SW/memrun/tree/master/C#adding-tcc--run-option-to-gcc-and-g
Just nice, and nothing gets stored in filesystem:
pi#raspberrypi400:~/memrun/C $ uname -a | g++ -O3 -Wall -run demo.cpp 42
bar 42
Linux raspberrypi400 5.10.60-v7l+ #1449 SMP Wed Aug 25 15:00:44 BST 2021 armv7l GNU/Linux
pi#raspberrypi400:~/memrun/C $

List all functions declared in header but missing in the source file?

Question
Are there some linters/statical analyzers that warn/error on functions, that are declared in the header file but not implemented in the corresponding source file?
Lets say we have the following header (guard omitted):
/* example.h */
int doSomething(int i);
double doSomethingElse(double d);
And the following source:
/* example.c */
#require "example.h"
int doSomething(int i) {
return i + 1;
}
So is there some tool, that can tell me that doSomethingElse() is missing in example.c?
Why asking?
In an exercise we got some headerfiles with a fully fletched interface, and partially prepared sourcefiles, with some functions beeing fully provided, some functions beeing partially provided, and some missing.
For actually running and compiling this programm it was enough to complete the partially provided functions, but still there is some discrepancy between the defined interface in the header and the now provided functions in the source file.
I could go through all header/source pairs by hand and implement the missing funtions, but it would be nice to have some autogenerated todolist.
I'd just do it with grep etc.:
grep ');' foo.h | tr -d ';' | while read decl
do
if ! grep -q "$decl" foo.c
then
echo "not found: $decl"
fi
done
No, this isn't perfect, but it might work if your use case is as simple as you've outlined.

unused functions detection utility for c

I am trying to measure my code coverage utilization on a C project consist of several libraries, and main program.
Is there a utility that can help me find which function I dont use from both libraries and main program.
I want to build list of functions (public functions) that are not used by my main program, in order to ignore them in my code coverage report.
If you are using gcc you compile your code with option:
-Wunused-function
Warn whenever a static function is declared but not defined or a non-inline static function is unused. This warning is enabled by -Wall.
cflow can create a call graph for the program, but it doesn't work well with pointers to functions in some cases.
for eaxample:
#include <stdio.h>
static int f1(){
return 1;
}
int (*p_f1)() = f1;
int main() {
p_f1();
return 0;
}
There are coverage tools available for free - for example "gcov" that runs on go with the gcc tool suite. However, Code Coverage only tells you which functions get hit by your testing (or whatever you do to excercise the code), so for example
ptr = malloc(...);
if (!ptr)
{
allocation_failed(__FILE__, __LINE__);
}
would only show that allocation_failed is called if you are also using some tool that makes your allocations fail from time to time.
I'm not aware of a tool that will show you what functions are not used across larger systems (with multiple libraries, etc). I expect you could make something by using the output of "nm" and a bit of "pulling things in". It won't cover foo and bar as unusued in this case:
unit1.c:
extern int foo(void);
int bar()
{
return foo();
}
unit2.c:
int foo(void)
{
return 42;
}
int baz(void)
{
return bar();
}
and then baz isn't used anywhere. But if you remove baz, it will show that bar is not called, and then you can remove foo after that...
Edit: Crazy idea time - how about taking every C file in the project and just concatenating the whole thing into a a single .C file, then add static at the beginning of every function, and compiling with -Wunused-functon - I'm sure there will be some "interesting" effects from this if your code isn't extremely well written, but it may be worth a try [it would be fairly easy to do this in a Linux system, something like find . -name "*.c" -print | xargs cat {} > giantsource.c - you then need a little bit of sed or something to label all functions static, which I'm not quite sure how you'd go about doing - it depends very much on the formatting of your code.
You may want to have a look at this:
http://www.gedanken.demon.co.uk/cxref/
I haven't used it, but any decent cross referencing tool should be able to identify anything that is "not used" as not having any references. Of course, you'll probably still have to run over the code severa times to weed out the functions that are used by functions that aren't being called, etc.
cflow has an option to build a cross-reference table: --xref
The format of the output is described by GNU cflow: Cross-Reference
GNU cflow is also able to produce cross-reference listings. This mode is enabled by --xref (-x) command line option. Cross-reference output lists each symbol occurrence on a separate line. Each line shows the identifier and the source location where it appears. If this location is where the symbol is defined, it is additionally marked with an asterisk and followed by the definition. For example, here is a fragment of a cross-reference output for d.c program:
printdir * d.c:42 void printdir (int level,char *name)
printdir d.c:74
printdir d.c:102
It shows that the function printdir is defined in line 42 and referenced twice, in lines 74 and 102.
To detect unused functions, search the line with a star not followed by a line with the same prefix. The following GNU Awk code print the unused functions:
{
if( $2 == "*" ) {
if( f ) {
print f
}
f = $1
}
else {
f = ""
}
}
The command may be:
cflow -x src/*.c src-gen/*.c | awk -f find-unused-functions.awk

Can the gcc C preprocessor use the output of a shell command?

For example, can the output of 'uname -a' be used to create the COMPILED_ON macro below?
#include <stdio.h>
#define COMPILED_ON `call uname -a and use the output as a quoted string'
int main( int argc, char **argv ) {
printf( COMPILED_ON );
return 0;
}
no, but:
gcc -DCOMPILED_ON="$(uname -a)"
I don't think that you can do that with the GNU preprocessor, but surely it's not doable with a plain standard preprocessor; instead, I think that this is the job for the Makefile.
Let it run uname -a and store it in a Makefile variable, that will be used to create the correct -D directive for the compiler.
You could also make the Makefile create a .h file that will contain the macro definition, and that file will be #included by the files that need the COMPILED_ON macro. This has the extra bonus of being independent of compiler-specific options to define macros.
Notice that these suggestions are applicable also to build tools other than the good ol' make.
Not like that, no.
You'd need to do:
gcc "-DCOMPILED_ON=\"`uname -a`\"" -c file.c -o file.o
Alternatively, have your makefile create a simple .h file:
echo "#define COMPILED_ON \"`uname -a`\"" > compiledon.h
Then #include "compiledon.h"
You'll need the \" part in order to get a usable string.
No, but you can achieve your objective in a less fragile and SCCS-hostile manner.
Have a make target run a shell script to create a .h file.
Have a make variable set itself via a shell command and pass down a -D. Not all make(1) implementations support this.
Have your make target run the compiler with a -D that incorporates the shell command.
If you happen to use CMake I have a nice snippet that might help some:
# This ensures the SHA1 git ref is shown within the app
execute_process(COMMAND git rev-parse HEAD WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE GITSHA1REF)
string(REGEX REPLACE "\n$" "" GITSHA1REF "${GITSHA1REF}")
message(STATUS "GITSHA1REF = ${GITSHA1REF}")
add_definitions("-DGITSHA1REF=\"${GITSHA1REF}\"")
With that I get the current Git SHA1 hash and use it like this in my C++ code:
std::cout << "Git-SHA1: " << GITSHA1REF << std::endl;

Resources