Including SVN revision of a project in C source code - c

How to include SVN revision of my project (not file revision) in C source code or in Makefile?

We use a line like this in our makefile:
REPO_REV := $(shell svnversion -n)
That stores the revision number of the working copy into a make variable. To use it in C code, you can have your makefile use that value to define a macro on the compiler command line (something like -DREPO_REV=$(REPO_REV) for gcc).

From the SVN book:
New users are often confused by how the $Rev$ keyword works. Since the repository has a single, globally increasing revision number, many people assume that it is this number that is reflected by the $Rev$ keyword's value. But $Rev$ expands to show the last revision in which the file changed, not the last revision to which it was updated. Understanding this clears the confusion, but frustration often remains—without the support of a Subversion keyword to do so, how can you automatically get the global revision number into your files?
To do this, you need external processing. Subversion ships with a tool called svnversion, which was designed for just this purpose. It crawls your working copy and generates as output the revision(s) it finds. You can use this program, plus some additional tooling, to embed that revision information into your files. For more information on svnversion, see the section called “svnversion—Subversion Working Copy Version Info” in Chapter 9, Subversion Complete Reference.

Related

Automatically find dependencies and create CMakeLists.txt with CMake (or CMake Tools in Visual Studio Code) [duplicate]

CMake offers several ways to specify the source files for a target.
One is to use globbing (documentation), for example:
FILE(GLOB MY_SRCS dir/*)
Another method is to specify each file individually.
Which way is preferred? Globbing seems easy, but I heard it has some downsides.
Full disclosure: I originally preferred the globbing approach for its simplicity, but over the years I have come to recognise that explicitly listing the files is less error-prone for large, multi-developer projects.
Original answer:
The advantages to globbing are:
It's easy to add new files as they
are only listed in one place: on
disk. Not globbing creates
duplication.
Your CMakeLists.txt file will be
shorter. This is a big plus if you
have lots of files. Not globbing
causes you to lose the CMake logic
amongst huge lists of files.
The advantages of using hardcoded file lists are:
CMake will track the dependencies of a new file on disk correctly - if we use
glob then files not globbed first time round when you ran CMake will not get
picked up
You ensure that only files you want are added. Globbing may pick up stray
files that you do not want.
In order to work around the first issue, you can simply "touch" the CMakeLists.txt that does the glob, either by using the touch command or by writing the file with no changes. This will force CMake to re-run and pick up the new file.
To fix the second problem you can organize your code carefully into directories, which is what you probably do anyway. In the worst case, you can use the list(REMOVE_ITEM) command to clean up the globbed list of files:
file(GLOB to_remove file_to_remove.cpp)
list(REMOVE_ITEM list ${to_remove})
The only real situation where this can bite you is if you are using something like git-bisect to try older versions of your code in the same build directory. In that case, you may have to clean and compile more than necessary to ensure you get the right files in the list. This is such a corner case, and one where you already are on your toes, that it isn't really an issue.
The best way to specify sourcefiles in CMake is by listing them explicitly.
The creators of CMake themselves advise not to use globbing.
See: https://cmake.org/cmake/help/latest/command/file.html?highlight=glob#glob
(We do not recommend using GLOB to collect a list of source files from your source tree. If no CMakeLists.txt file changes when a source is added or removed then the generated build system cannot know when to ask CMake to regenerate.)
Of course, you might want to know what the downsides are - read on!
When Globbing Fails:
The big disadvantage to globbing is that creating/deleting files won't automatically update the build-system.
If you are the person adding the files, this may seem an acceptable trade-off, however this causes problems for other people building your code, they update the project from version-control, run build, then contact you, complaining that"the build's broken".
To make matters worse, the failure typically gives some linking error which doesn't give any hints to the cause of the problem and time is lost troubleshooting it.
In a project I worked on we started off globbing but got so many complaints when new files were added, that it was enough reason to explicitly list files instead of globbing.
This also breaks common git work-flows(git bisect and switching between feature branches).
So I couldn't recommend this, the problems it causes far outweigh the convenience, when someone can't build your software because of this, they may loose a lot of time to track down the issue or just give up.
And another note, Just remembering to touch CMakeLists.txt isn't always enough, with automated builds that use globbing, I had to run cmake before every build since files might have been added/removed since last building *.
Exceptions to the rule:
There are times where globbing is preferable:
For setting up a CMakeLists.txt files for existing projects that don't use CMake.Its a fast way to get all the source referenced (once the build system's running - replace globbing with explicit file-lists).
When CMake isn't used as the primary build-system, if for example you're using a project who aren't using CMake, and you would like to maintain your own build-system for it.
For any situation where the file list changes so often that it becomes impractical to maintain. In this case it could be useful, but then you have to accept running cmake to generate build-files every time to get a reliable/correct build (which goes against the intention of CMake - the ability to split configuration from building).
* Yes, I could have written a code to compare the tree of files on disk before and after an update, but this is not such a nice workaround and something better left up to the build-system.
In CMake 3.12, the file(GLOB ...) and file(GLOB_RECURSE ...) commands gained a CONFIGURE_DEPENDS option which reruns cmake if the glob's value changes.
As that was the primary disadvantage of globbing for source files, it is now okay to do so:
# Whenever this glob's value changes, cmake will rerun and update the build with the
# new/removed files.
file(GLOB_RECURSE sources CONFIGURE_DEPENDS "*.cpp")
add_executable(my_target ${sources})
However, some people still recommend avoiding globbing for sources. Indeed, the documentation states:
We do not recommend using GLOB to collect a list of source files from your source tree. ... The CONFIGURE_DEPENDS flag may not work reliably on all generators, or if a new generator is added in the future that cannot support it, projects using it will be stuck. Even if CONFIGURE_DEPENDS works reliably, there is still a cost to perform the check on every rebuild.
Personally, I consider the benefits of not having to manually manage the source file list to outweigh the possible drawbacks. If you do have to switch back to manually listed files, this can be easily achieved by just printing the globbed source list and pasting it back in.
You can safely glob (and probably should) at the cost of an additional file to hold the dependencies.
Add functions like these somewhere:
# Compare the new contents with the existing file, if it exists and is the
# same we don't want to trigger a make by changing its timestamp.
function(update_file path content)
set(old_content "")
if(EXISTS "${path}")
file(READ "${path}" old_content)
endif()
if(NOT old_content STREQUAL content)
file(WRITE "${path}" "${content}")
endif()
endfunction(update_file)
# Creates a file called CMakeDeps.cmake next to your CMakeLists.txt with
# the list of dependencies in it - this file should be treated as part of
# CMakeLists.txt (source controlled, etc.).
function(update_deps_file deps)
set(deps_file "CMakeDeps.cmake")
# Normalize the list so it's the same on every machine
list(REMOVE_DUPLICATES deps)
foreach(dep IN LISTS deps)
file(RELATIVE_PATH rel_dep ${CMAKE_CURRENT_SOURCE_DIR} ${dep})
list(APPEND rel_deps ${rel_dep})
endforeach(dep)
list(SORT rel_deps)
# Update the deps file
set(content "# generated by make process\nset(sources ${rel_deps})\n")
update_file(${deps_file} "${content}")
# Include the file so it's tracked as a generation dependency we don't
# need the content.
include(${deps_file})
endfunction(update_deps_file)
And then go globbing:
file(GLOB_RECURSE sources LIST_DIRECTORIES false *.h *.cpp)
update_deps_file("${sources}")
add_executable(test ${sources})
You're still carting around the explicit dependencies (and triggering all the automated builds!) like before, only it's in two files instead of one.
The only change in procedure is after you've created a new file. If you don't glob the workflow is to modify CMakeLists.txt from inside Visual Studio and rebuild, if you do glob you run cmake explicitly - or just touch CMakeLists.txt.
Specify each file individually!
I use a conventional CMakeLists.txt and a python script to update it. I run the python script manually after adding files.
See my answer here:
https://stackoverflow.com/a/48318388/3929196
I'm not a fan of globbing and never used it for my libraries. But recently I've looked a presentation by Robert Schumacher (vcpkg developer) where he recommends to treat all your library sources as separate components (for example, private sources (.cpp), public headers (.h), tests, examples - are all separate components) and use separate folders for all of them (similarly to how we use C++ namespaces for classes). In that case I think globbing makes sense, because it allows you to clearly express this components approach and stimulate other developers to follow it. For example, your library directory structure can be the following:
/include - for public headers
/src - for private headers and sources
/tests - for tests
You obviously want other developers to follow your convention (i.e., place public headers under /include and tests under /tests). file(glob) gives a hint for developers that all files from a directory have the same conceptual meaning and any files placed to this directory matching the regexp will also be treated in the same way (for example, installed during 'make install' if we speak about public headers).

Reading a set of variables into a Makefile from a C source file?

I have an AVR8 GCC application that can be built with a standard makefile. Because some folks who want to build the application don't want to set up make and such (or have trouble doing so), I also have figured out how to set the project up so it can be compiled from the Arduino IDE as well.
All is working.
But, I normally set some items in the makefile, like the version number and such, but creating the VERSION string in the makefile and passing it as a define into each source file compilation. But, when run from the Arduino IDE, that step is obviously not occurring. So, I have to create a second #define in the Arduino sketch stub to recreate the define.
This means when I update the version, I need to do so in 2 places, in the makefile and in the source file.
The easy option is to simply move the VERSION creation to the source file, where both can use it. And, I'm OK doing that, but
The makefile actually needs the version information, both to create the right filename (think app_v1.2.3.4.bin) and embed the version number into the bin file since it is used by the boot-loader (if requested) to ensure the version the boot-loader flashes is newer than the one already in FLASH. So, if I move the VERSION, RELEASE, MODIFICATION, etc. defines into the C code, I need to find a way to pull them back into the makefile.
I tried using the file read operations in the makefile, but they seem to ignore:
#define VERSION 0
with the prefaced '#' char.
I see there's some options to run sed/awk/etc, in bash, but I don't want to make too many assumptions on the environment, and the makefile currently runs on Windows as well as Unix/Linux without any differences.
I tried a few stack overflow examples, but nothing seems to yield those 4 numbers from any file, .h or otherwise.
I'm OK with creating version.h with just:
#define VERSION 0
#define RELEASE 1
#define MODIFICATION 2
#define FIX 4
If I can read it into the makefile and create the variables I need.
Jim
You may take a look at gmtt which was designed exactly with you use case in mind. In gmtt the following should read and analyze your header file:
include gmtt.mk
# create a 3-column table from the header file. The first column is just the "#define"
VNR_TABLE := 3 $(file < version.h)
# Extract the values from the table: select column 3 from VNR_TABLE where column 2 equals a string constant.
# Be careful not to introduce spaces in the compare!
VER := $(call select,3,$(VNR_TABLE),$$(call str-eq,$$2,VERSION))
REL := $(call select,3,$(VNR_TABLE),$$(call str-eq,$$2,RELEASE))
MODF := $(call select,3,$(VNR_TABLE),$$(call str-eq,$$2,MODIFICATION))
FIX := $(call select,3,$(VNR_TABLE),$$(call str-eq,$$2,FIX))
I couldn't test it but I think you get the idea.
PS: using a GNUmake library just means placing the included file alongside the makefile.
I think in this case you can use the ‘file’ function of makefiles.
It allows you to write (with > specifier) or read (with < specifier) to/from files. Then you can trim (with filter-out) your variables inside your makefile.
Source: https://www.gnu.org/software/make/manual/html_node/File-Function.html#File-Function
You can use GNU make's $(shell ...) function to extract the macro expansions. Assuming VERSION is defined in src.c and tokens are delimited by spaces (not tabs):
VERSION := $(shell sed -n -e "s/^\#define VERSION *\(.*\)/\1/p" src.c)
.PHONY: all
all:
#echo VERSION=$(VERSION)

Default extension for message catalog files

I want to localize my application using the catopen()/catgets() family of functions.
As far as I understand, in the absence of NLSPATH variable, message catalogs will be looked up under /usr/share/locale/xx_YY/LC_MESSAGES.
What is the "traditional" file extension for message catalog files? I see some code examples using *.cat while others don't use any extension at all. Is it dependent on a particular UNIX flavour?
On my Linux boxes I see plenty of *.mo files, but those are GNU gettext archives. It seems catgets() can rarely be seen "in the wild" nowadays.
I meant this to be a comment, but it's a bit too long :P
Looking at the doc you've linked to, it seems probably that the code isn't opinionated as to file extension. Since you're not using MIME or anything to automatically find a handler for this file, the only requirement is likely to be that the name is correct. In UNIX, especially in the shell, file extensions often mean nothing to the system - fo example, any file extension can be used on an executable script as long as the executable bit is set and the shebang line at the top of the file specifies an appropriate interpreter.
It's possible the user community, if one still exists for this crufty sounding library, has a standard naming convention that the docs don't describe - but I wouldn't sweat it too much. It's trival to change file names, even if it means a recompile ( command line variables would make the program agnostic as to file name and extension )

Embed the image build time in code

We constantly run into issues where we are running different C images on different machines. Since our code is still in production, we keep generating binaries that folks use. Often lot of time is spent in debugging only to learn later that the binaries were incorrect.
I was wondering if there is some mechanism with which I could print the date and time when I had compiled the code then we could use that to ensure that all binaries are the same.
Any idea on how I can do this in C? I want to print the time and date when I built this image along with the version number.
The __DATE__ and __TIME__ macros are predefined by the compiler with the date and time that a C program is built. These are just string literals of the format "Mmm dd yyyy" and "hh:mm:ss" respectively, so should be easy to use with any logging or printing system. (These macros are defined in the C11 standard section 6.10.8.1)
It's called versioning:
First, add a dedicated source file which uses the compile time strings __DATE__ and __TIME__.
For example:
printf("%s %s",__DATE__,__TIME__);
Or if you want to be able to extract information from these strings:
char _date[] = __DATE__; // the format is "Jan 1 2000"
char _time[] = __TIME__; // the format is "00:00:00"
// Now use these variables to extract the data...
Then, make sure that whenever you change any file in your project, the dedicated source file will be recompiled. You can typically apply this in your project settings pre-build configuration, by invoking a script which deletes the object file corresponding to the dedicated source file.
For example, let's assume that the name of the dedicated source file is version.c, the extension of object files is obj, and the location of object files relatively to the project is debug\obj.
If you are running your build tools over Windows, then you may invoke the following batch file:
set VERSION_FILE="debug\obj\version.obj"
if exist %VERSION_FILE% del /q %VERSION_FILE%

Injecting mercurial changeset as version information in a C executable

I would like the executables for a project I am working on to have the latest mercurial changeset recorded so that when a user complains about buggy behavior, I can track which version they are using. Some of my executables are Python and others are compiled C. Is there a way to automate this, or can you point me to projects that exhibit solutions that I can look at?
I am using autoconf in my project... in case that makes the solution easier.
Thanks!
Setjmp
A common way to do this is with m4_esyscmd. For example, autoconf distributes a script in build-aux which generates a version number from the git repo and invokes AC_INIT as:
AC_INIT([GNU Autoconf], m4_esyscmd([build-aux/git-version-gen .tarball-version]),
[bug-autoconf#gnu.org])
You can often get away without distributing the script and do something simple like:
AC_INIT([Package name], m4_esyscmd([git describe --dirty | tr -d '\012']),
[bug-report-address])
Instead of git-describe, use whatever command you want to generate the version number. One important detail is that it should not have a trailing newline (hence the tr following git-describe).
A major drawback with this technique is that the version number is only generated when you run autoconf.
Add this to configure.ac:
AM_CONDITIONAL([IS_HG_REPO], [test -d "$srcdir/.hg"])
Add the following lines to Makefile.am:
if IS_HG_REPO
AM_CPPFLAGS = -DHGVERSION="\"$(PACKAGE) `hg parents --template 'hgid: {node|short}'`\""
else
AM_CPPFLAGS = -DHGVERSION=PACKAGE_STRING
endif
This will define HGVERSION as a string of the form APPNAME hgid: 24d0921ee4bd or APPNAME VERSION, if building from a release tarball.
See wiki page on versioning with make

Resources