How to check the OS with automake - linux

I have a project that uses automake to create the configure and all related files (I'm using autoreconf command to make all this stuff). So, I'm trying to set some conditional files to compile when the project is compiling for macOS (OS X), Windows or Linux. But it fails with the following:
$ autoreconf -i ..
src/Makefile.am:30: error: LINUX does not appear in AM_CONDITIONAL
autoreconf: automake failed with exit status: 1
And the part containing the error in that Makefile.am is the following:
if OSX
butt_SOURCES += CurrentTrackOSX.h CurrentTrackOSX.m
endif
if LINUX
butt_SOURCES += currentTrack.h currentTrackLinux.cpp
endif
if WINDOWS
butt_SOURCES += currentTrack.h currentTrack.cpp
endif
My question is, how can I check if the OS is Linux? And if it's possible, is there a better way to check the OS in automake?

You can detect it directly in the Makefile, or define the conditionals in the configure source file (probably configure.ac), since you are using autoreconf:
# AC_CANONICAL_HOST is needed to access the 'host_os' variable
AC_CANONICAL_HOST
build_linux=no
build_windows=no
build_mac=no
# Detect the target system
case "${host_os}" in
linux*)
build_linux=yes
;;
cygwin*|mingw*)
build_windows=yes
;;
darwin*)
build_mac=yes
;;
*)
AC_MSG_ERROR(["OS $host_os is not supported"])
;;
esac
# Pass the conditionals to automake
AM_CONDITIONAL([LINUX], [test "$build_linux" = "yes"])
AM_CONDITIONAL([WINDOWS], [test "$build_windows" = "yes"])
AM_CONDITIONAL([OSX], [test "$build_mac" = "yes"])
Note: host_os refers to the target system, so if you are cross-compiling it sets the OS conditional of the system you are compiling to.

Have you tried using the lsb_release command to determine the type and version of operating system ?

Related

Cmake check if the current platform is Fedora [duplicate]

Does anybody know any cmake variable or hook or something which can give me underlying platform name/flavour name on which it is getting executed ?
e.g. Linux-CentOs
Linux-Ubuntu
Linux-SLES
I know cmake has "CMAKE_SYSTEM" variable but that doesn't help differentiating flavours of linux for e.g. Any help is appreciated.
edit :
I just read that it can be done using lsb_release command ?
The following snippet populates the LSB_RELEASE_ID_SHORT cmake variable with information about the underlying Linux system:
find_program(LSB_RELEASE_EXEC lsb_release)
execute_process(COMMAND ${LSB_RELEASE_EXEC} -is
OUTPUT_VARIABLE LSB_RELEASE_ID_SHORT
OUTPUT_STRIP_TRAILING_WHITESPACE
)
On Ubuntu, for example, it yields Ubuntu.
Slightly less convoluted than checking files on the filesystem is to deduce the best you can from the available CMAKE_SYSTEM vars. For instance a CMakeLists.txt file containing lines like this:
message("-- CMAKE_SYSTEM_INFO_FILE: ${CMAKE_SYSTEM_INFO_FILE}")
message("-- CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}")
message("-- CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}")
message("-- CMAKE_SYSTEM: ${CMAKE_SYSTEM}")
string (REGEX MATCH "\\.el[1-9]" os_version_suffix ${CMAKE_SYSTEM})
message("-- os_version_suffix: ${os_version_suffix}")
outputs this when I ran cmake . :
-- CMAKE_SYSTEM_INFO_FILE: Platform/Linux
-- CMAKE_SYSTEM_NAME: Linux
-- CMAKE_SYSTEM_PROCESSOR: x86_64
-- CMAKE_SYSTEM: Linux-2.6.32-573.7.1.el6.x86_64
-- os_version_suffix: .el6
And for my situation, the .el6 was enough to differentiate.
Likely, you have to write such a test yourself. Here's one of the possible examples, just googled: https://htcondor-wiki.cs.wisc.edu/index.cgi/fileview?f=build/cmake/FindLinuxPlatform.cmake&v=4592599fecc08e5588c4244e2b0ceb7d32363a56
However depending on your actual needs the test may be quite complex. For example Ubuntu as a Debian-based OS always has /etc/debian_version and many RPM-based OSes traditionally have /etc/redhat-release. There's a file /etc/os-release in the Linux Standard Base (LSB) specification, but for example on the localhost this file is empty for an unknown reason :)
I know this is an old question, but as of now, there is still no cmake built-in function to find this information in good detail. I've implemented a small utility function that uses lsb_release on Linux to find a number of relevant system details:
function(get_linux_lsb_release_information)
find_program(LSB_RELEASE_EXEC lsb_release)
if(NOT LSB_RELEASE_EXEC)
message(FATAL_ERROR "Could not detect lsb_release executable, can not gather required information")
endif()
execute_process(COMMAND "${LSB_RELEASE_EXEC}" --short --id OUTPUT_VARIABLE LSB_RELEASE_ID_SHORT OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND "${LSB_RELEASE_EXEC}" --short --release OUTPUT_VARIABLE LSB_RELEASE_VERSION_SHORT OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND "${LSB_RELEASE_EXEC}" --short --codename OUTPUT_VARIABLE LSB_RELEASE_CODENAME_SHORT OUTPUT_STRIP_TRAILING_WHITESPACE)
set(LSB_RELEASE_ID_SHORT "${LSB_RELEASE_ID_SHORT}" PARENT_SCOPE)
set(LSB_RELEASE_VERSION_SHORT "${LSB_RELEASE_VERSION_SHORT}" PARENT_SCOPE)
set(LSB_RELEASE_CODENAME_SHORT "${LSB_RELEASE_CODENAME_SHORT}" PARENT_SCOPE)
endfunction()
Add it to your CMakeLists.txt and use it like this:
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
get_linux_lsb_release_information()
message(STATUS "Linux ${LSB_RELEASE_ID_SHORT} ${LSB_RELEASE_VERSION_SHORT} ${LSB_RELEASE_CODENAME_SHORT}")
endif()
If you need further details, check what else lsb_release can provide with lsb_release -a.
Note that not every Linux has lsb_release installed. Most systems provide it, but its not mandatory. On newer Ubuntu, for example, its the default on desktop installs, and required by ubuntu-minimal. If it should be missing on your machine, you can install it with sudo apt install lsb-release.
on my machine
CMAKE_SYSTEM_INFO_FILE == "Platform/Linux"
CMAKE_SYSTEM_NAME == "Linux"
CMAKE_SYSTEM == "Linux-<kernel version>"
obtained with cmake --system-information, I know of people that use said macros in their own CMakeLists.txt files so they work as expected, probably CMAKE_SYSTEM_NAME is what you really want but here you go, you get this 3 and the command to inspect the properties of your machine as far as cmake is concerned .
Based on thiagowfx answer, If you want to get the codename of the distro (if it is available):
execute_process(COMMAND lsb_release -cs
OUTPUT_VARIABLE RELEASE_CODENAME
OUTPUT_STRIP_TRAILING_WHITESPACE
)
E.g. in Ubuntu 14.04 the variable RELEASE_CODENAME will hold trusty.
### find our os (wins, centos, ubuntu, etc)
set(VAR_OS "")
IF(CMAKE_SYSTEM_NAME MATCHES "Linux")
MESSAGE(DEBUG "Linux")
execute_process (
COMMAND bash -c "awk -F= '/^ID=/{print $2}' /etc/os-release |tr -d '\n' | tr -d '\"'"
OUTPUT_VARIABLE outOS
)
MESSAGE(DEBUG "Linux os: ${outOS}")
ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Windows")
MESSAGE(DEBUG "Windows")
set(VAR_OS "wins")
ELSE()
MESSAGE(FATAL "No OS detected!")
ENDIF(CMAKE_SYSTEM_NAME MATCHES "Linux")

Unchanged shell script arguments propagation

The primary problem running either 32 or 64 bit binaries depending on the current platform. I have predefined set of platforms (precisely 2: 32- and 64-bit Debian, both running 64-bit kernel, but all user-space is 32- or 64-bit respectively) and >100 platform-specific binaries. All these binaries are called from my wrapper script, which is identical for all of them.
I have 2 directories: bin and tmp. In bin there are links to wrapper script and script itself. In tmp there are 2 directories: 32 and 64. In these directories executables and libraries are placed.
I've wrote the following script that relies on exported by Bash environment variable HOSTTYPE:
#!/bin/bash
executable=`basename $0`
tmp_path=$(readlink -f $(dirname $(readlink -f $0))/../tmp)
case "$HOSTTYPE" in
i?86 )
export LD_LIBRARY_PATH="$tmp_path/32:$LD_LIBRARY_PATH"
exec "$tmp_path/32/$executable" $#
;;
x86_64 )
export LD_LIBRARY_PATH="$tmp_path/64:$LD_LIBRARY_PATH"
exec "$tmp_path/64/$executable" $#
;;
* )
echo "Unknown host type: '$HOSTTYPE'"
;;
esac
Most of the time it works as expected but it gets broken when the argument that should be quoted is passed. For example one calls
./some_binary --argument="\t" filename
where \t is a tab character. The script receives the pre-interpreted arguments list as:
./some_binary --argument=\t filename
Then it is reinterpreted once again in exec as
../tmp/32/some_binary --argument= filename
The tab characted get lost. How can I prevent this? Or should I solve my primary problem in a drastically different way?

Linux makefile to check required minimum linux kernel version

I have a linux makefile that needs to compile on 2 different kernel versions. The makefile is not generated from automake/autoconf.
The C code are already conditioned using macros to generate different code for different kernel version, but certain features in the makefile needs to be also conditioned.
Is there a way in a makefile to do:
if (kernel_version > 2.6.30)
newer_kernel = 1
else
newer_kernel = 0
endif
Well, I can think of one quick way to do it with bash:
KERNEL_VERSION=`uname -r`
HIGHER_VERSION=`echo -e "$KERNEL_VERSION\n2.6.30" | sort -g -t '.' | tail --lines=1`
if [ "$HIGHER_VERSION" == "2.6.30" ]
# its an older kernel
else
# its a newer kernel
fi
Basically, you use uname to obtain the version of the current kernel, then compare it to 2.6.30 using sort (the -g flag enables numeric sorting, -t '.' means use dot as a field separator), then use tail to determine which of the two version was higher in the list. Not exactly a beautiful solution, but it will work.
You can put it into a separate script or directly into the makefile recipe

Is it possible to define macros in makefiles conditionally?

I'm writing a makefile that has to be compatible with both LINUX and the HP-UX operating system. I'm aware that certain shell commands in LINUX are not compatible with HP-UX. Consequently, I was wondering if it was possible to have macros declared conditionally so that if it was determined that the OS was HP-UX, the macro would be defined a certain way and if the OS was LINUX, it would be defined differently?
OS = `uname`
myOS = Linux
ifeq ($(OS),$(myOS))
message = "HELLO LINUX"
else
message = "HELLO FOO"
endif
all: install
install:
echo $(message)
I've tried using the approach above; however, it seems that ifeq determines that OS and myOS are not the same. They should both be 'Linux', but it's outputting the else block instead.
You shall use $(shell ...) in order to execute a SHELL command, this will work
OS := $(shell uname)
myOS := Linux
ifeq ($(OS),$(myOS))
message := "HELLO LINUX"
else
message := "HELLO FOO"
endif
all: install
install:
echo $(message)
Yes, you can define conditionals in makefiles.
This example taken from the above link
libs_for_gcc = -lgnu
normal_libs =
foo: $(objects)
ifeq ($(CC),gcc)
$(CC) -o foo $(objects) $(libs_for_gcc)
else
$(CC) -o foo $(objects) $(normal_libs)
endif
This shows the syntax for conditionals.
Given this defining anything specific should not be a problem. E.g., one could define/pass on marcos via the -D switch for a C program.
Update: To fix your problem with the OS variable not getting the output of the shell command uname you need to use shell function (as correctly pointed out by #AraundF): To quote from the link I posted:
"The shell function performs the same function that backquotes (``)
perform in most shells ..."
so you were on the right track.
What we used to do here is we define an environment variable ARCH on all systems we build stuff on, on a Linux system it will have value linux, on AIX aix, etc., in the Makefile we have:
include make.$(ARCH)
and for each platform we create a file called make.linux, make.aix, etc., with definitions specific for that platform, for example make.linux contains:
CC=g++
and make.aix contains
CC=xlC_r
This is quite a nice and clean approach, but nevertheless we are migrating to cmake ( http://www.cmake.org/ ) now.

Library resolution with autoconf?

I'm building my first autoconf managed package.
However I can't find any simple examples anywhere of how to specify a required library, and find that library where it might be in various different places.
I've currently got:
AC_CHECK_LIB(['event'], ['event_init'])
but:
It doesn't find the version installed in /opt/local/lib
It doesn't complain if the library isn't actually found
I need to set the include path to /opt/local/include too
any help, or links to decent tutorials much appreciated...
autoconf script cannot guess the "optional" library locations, which may vary from one platform to another. So you can say
CPPFLAGS="-I/opt/local/include" LDFLAGS="-L/opt/local/lib" ./configure
For AC_CHECK_LIB() you need to specify the fail condition explicitly in "action-if-false" argument:
dnl This is simply print "no" and continue:
AC_CHECK_LIB([m], [sqrt123])
dnl This will stop:
AC_CHECK_LIB([m], [sqrt123], [], [AC_MSG_ERROR([sqrt123 was not found in libm])])
Output:
checking for sqrt123 in -lm... no
checking for sqrt123 in -lm... no
configure: error: sqrt123 was not found in libm
AC_CHECK_LIB() does not fail by default on obvious reasons: one may check for several different libraries that provide similar functionality and choose one of them :)
Also have a look at this post for similar topic.
You need to manually set CFLAGS, CXXFLAGS and LDFLAGS if you want gcc/g++ to look in non-standard locations.
So, before calling AC_CHECK_LIB(), do something like
CFLAGS="$CFLAGS -I/opt/local/include"
CXXFLAGS="$CXXFLAGS -I/opt/local/include"
LDFLAGS="$LDFLAGS -L/opt/local/lib"
You don't need CXXFLAGS if you're only using gcc throughout your configure script.
If the library ships a .pc file, consider using the PKG_CHECK_MODULES() macro which does the things you want. If it's your own library, just ship a .pc file into /usr/lib/pkgconfig, it'll make it much easier for other developers to depend/use it.
I know this is an old thread now, but I guess this may help some people out. This is how I find some stuff.
hdff="no"
hdffprefix="ERROR"
AC_ARG_WITH(hdf,[ --with-hdf Compile with hdf library, for output.],[hdffprefix=$withval hdff="yes"],[])
# if there is no value given, it appears tha hdffprefix is set to "yes"
if test $hdffprefix = "yes" -a $hdff = "yes"
then
echo "HDF: Attempting to find HDF"
hdffprefix="ERROR"
# check if hdffprefix is set, if it is not, it sets it to "ERROR" and the
# 'if' comparison evaluates to true
if [[ "$hdffprefix" == "ERROR" ]]
then
echo "HDF: hdffprefix not set, searching PATH"
for i in `echo $PATH | tr ':' '\n'`
do
if [[ $i == *hdf* ]]
then
if [[ $i == *bin/* ]]
then
hdffprefix=${i%bin/}
# if it doesn't exist, re-set to ERROR
if [[ ! -f ${hdffprefix}include/hdf.h ]]
then
hdffprefix="ERROR"
fi
elif [[ $i == *bin* ]]
then
hdffprefix=${i%bin}
# if it doesn't exist, re-set to ERROR
if [[ ! -f ${hdffprefix}include/hdf.h ]]
then
hdffprefix="ERROR"
fi
fi
fi
done
if [[ "$hdffprefix" == "ERROR" ]]
then
echo "HDF: hdffprefix not found in PATH, trying 'which'"
WHICH_TEST_HDF=`which hdf2gif`
if [[ WHICH_TEST_HDF != "" ]]
then
hdffprefix=${WHICH_TEST_HDF%bin/hdf2gif}
else
echo "HDF: Warning - hdf not found"
fi
fi
fi
if [[ "$hdffprefix" != "ERROR" ]]
then
hdff="yes"
echo "HDF found: $hdffprefix"
fi
fi
if test $hdff = 'yes'; then
hdfincs=" -DUSE_HDF -I"${hdffprefix}"include"
scriptotherlibsinc=${scriptotherlibsinc}" -L"${hdffprefix}"/lib"
scriptotherlibs=${scriptotherlibs}" -lmfhdf -ldf -ljpeg -lz"
AC_CHECK_HEADERS([${hdffprefix}/include/hdf.h],,[AC_MSG_ERROR([Cannot find hdf.h])])
AC_CHECK_HEADERS([${hdffprefix}/include/mfhdf.h],,[AC_MSG_ERROR([Cannot find mfhdf.h])])
fi
Here's how to do it:
# We need the math library for some tests.
AC_CHECK_LIB([m], [floor], [],
[AC_MSG_ERROR([Can't find or link to the math library.])])
Note that it does not automatically error out when the library is not found, you must called AC_MSG_ERROR() as in the code above.
So you want to setup autoconf to find these directories automatically and codelogic gives the answer; but suppose you don't want to search there on all system, only on a mac. You can add the following
AC_CANONICAL_HOST
case $host_os in
darwin* )
CFLAGS="$CFLAGS -I/opt/local/include"
CXXFLAGS="$CXXFLAGS -I/opt/local/include"
LDFLAGS="$LDFLAGS -L/opt/local/lib"
;;
esac
Note that I added it as a case tree so that you can add things for a variety of operating systems later (such as linux* and BSD).
If you are happen to be using GCC or CLANG, the standard way is having the environment variable CPLUS_INCLUDE_PATH with the path of the non-official includes files and LIBRARY_PATH for the libraries. Remind that you do not have to change anything in the configure.ac. So you can just call the configure in this way:
$ export CPLUS_INCLUDE_PATH=/opt/local/include
$ export LIBRARY_PATH=/opt/local/lib
$ ./configure
The facto standard variables
Variable | lang | Usage
-------------------|------|---------
C_INCLUDE_PATH | C | colon separated list of include directory paths
CPLUS_INCLUDE_PATH | C++ | colon separated list of include directory paths
LIBRARY_PATH | C/C++| colon separated compiling time static linking dirs
LD_RUN_PATH | C/C++| colon separated compiling time dynamic linking dirs
LD_LIBRARY_PATH | C/C++| colon separated run-time dynamic linking dirs
CPPFLAGS | C/C++| prepocessor flags
CFLAGS | C | Compiling flags
CXXFLAGS | C++ | Compiling flags
LDFLAGS | C++ | Linking flags
NOTE You can use CPPFLAGS or LDFLAGS, however, CPLUS_INCLUDE_PATH /LIBRARY_PATH exactly fits your requirement. CPPFLAGS/LDFLAGS are for flags which can be many things but *_PATH are for PATHs
Portability Note: While this will work on many modern compilers, not all compilers will respect these variables. Some cross-compilers will outright ignore or overwrite them, which forces one to resort to CFLAGS and LDFLAGS modifications as mentioned in other answers.
SOURCE Might the downvotes here be because of the lack of sources in my answer. Here is for CPLUS_INCLUDE_PATH in GCC: https://gcc.gnu.org/onlinedocs/cpp/Environment-Variables.html

Resources