8

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.

linuxbuild
  • 15,843
  • 6
  • 60
  • 87
Pirks
  • 322
  • 2
  • 4
  • 16

5 Answers5

12

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.

jheddings
  • 26,717
  • 8
  • 52
  • 65
  • 2
    The only issue with the MYLIB_VERSION_CHECK that I see is that it is evaluated at compile-time, and a good optimizer will remove the check if everything is OK, and will unconditionally call printf() - you meant fprintf(stderr, ...), didn't you? - and exit(). I think it would be better to call a function that embedded the logic rather than use a constant expression. – Jonathan Leffler Oct 28 '09 at 16:30
  • Yeah, good points here... I later edited my post to include embedding the information in the library for this reason. Thanks for the clarification. – jheddings Oct 28 '09 at 16:38
10

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
Puppe
  • 4,995
  • 26
  • 27
  • You would, of course, make that `const char library_version[] = "1.3.6";` to save the space for the pointer. And you would declare the variable in the library's header (or in a header distributed with the library). – Jonathan Leffler Oct 28 '09 at 16:26
  • 1
    Added info for checking the version from a shell – Puppe Oct 28 '09 at 17:02
3

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.

jheddings
  • 26,717
  • 8
  • 52
  • 65
  • @vitaly.v.ch Not available on anything but linyux. – MarcusJ Jun 02 '17 at 03:43
  • @MarcusJ it's trivial to cross-compile ident for everything – vitaly.v.ch Jun 02 '17 at 07:44
  • Don't curr. you've simply moved the complexity from our hands to a third parties, and added a dependency in the process. I'm glad it works for you, but you're ignorant if you think that solution is applicable to everyone. – MarcusJ Jun 02 '17 at 07:56
2

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)

technosaurus
  • 7,676
  • 1
  • 30
  • 52
1

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. :)

Community
  • 1
  • 1
Jesse Chisholm
  • 3,857
  • 1
  • 35
  • 29