Makefiles and cross platform development - c

I have been trying to figure out how to create a C program that can be compiled for all of the major operating systems. I have considered using makefiles so I would just have to change the target OS, but I have a few problems. My first problem is that I cannot figure out how to change the target OS so I can compile on one OS but use the application on all OS's. My second issue is that I cannot figure out how to make it automatically compile all .c files in the src directory, so I do not have to modify the makefile every time I add a new file. Does anybody know how to do this stuff?
My current makefile (currently unmodified from here)
CC=gcc
CFLAGS=-c -Wall
LDFLAGS=
SOURCES=main.cpp hello.cpp factorial.cpp
OBJECTS=$(SOURCES:.cpp=.o)
EXECUTABLE=hello
all: $(SOURCES) $(EXECUTABLE)
$(EXECUTABLE): $(OBJECTS)
$(CC) $(LDFLAGS) $(OBJECTS) -o $#
.cpp.o:
$(CC) $(CFLAGS) $< -o $#q2

The GNU Autoconf is a good place to start. I can't give you a complete answer because I don't know what you'd do for Windows except maintain a set of nmake scripts. While there's a learning curve they make it possible to write make files and c-code that work across platforms. It will not only handle multiple source files for you but help you define target such as 'all', 'install,' 'clean,' etc. If you're familiar with downloading a project and then typing './configure' and then 'make install' then you've probably been a user of an Autoconf generated project.
There are other reasons to use Autoconf than just handling multiple source files.
What makes C/C++ development across flavors of Unix difficult is the definition of 'Unix' and C. Different flavors of Unix have slightly different library or system calls depending on what flavor of standards they support. The same is true of C. So, when writing code you'll see #defines specifying a group of preprocessor symbols that define a version of Unix and C that you'd like to use. Going from GCC to HP C or some other vendor's C compiler may include different 'default' levels of behavior, so you need to make the expected behavior explicit.
Second, you need to define what libraries are required. In your build you might need to probe for mysql libraries and decide what to do if those libraries are not installed. In some cases you might need to not build at all or in some cases you might just substitute for another library (like libdb). Having tried writing complex makefiles to support even two operating systems (Solaris and Linux), I would not want to re-invent this wheel.

Related

Is it a compiled library portable?

I have a conceptual question about writing a library in plain c. I have some functions that I have to use in different programs in the same folder, so I was thinking about writing a library to host these functions. I have to write the whole code in a folder that will be copied to another computer (where the programs will run). If I create and compile the library in this folder, will be the user able to run the programs without rebuilding the library from source or he might have some unpredictable errors? The user will build the programs that use the library anyway, he won't build the lib itself.
Thanks
Lorenzo
In general, no, it is not portable in the sense that a compiled library can be linked on an arbitrary other system. The compiled library has to be compatible to the target architecture, the OS, the compiler system, to name some.
But you have another choice, concluded from your comment: It seems that you also provide some shell script or makefile to build the programs.
Because a library consists of "just" a set of compiled translation units before some of them get linked into the programs, you can take the set of sources of these translations unit and compile them with the sources of each program, where appropriate.
As an example, let's say you have 2 functions (each in its own source file) you use in different combinations in 3 programs. "prg1" uses func1(), "prg2" uses func2(), and "prg3" uses both.
This can be the commands to build the programs with a (static) library:
gcc -c func1.c -o func1.o
gcc -c func2.c -o func2.o
ar -r lib.a func1.o func2.o
gcc prg1.c lib.a -o prg1
gcc prg2.c lib.a -o prg2
gcc prg3.c lib.a -o prg3
Instead of the library you compile the programs' sources directly:
gcc prg1.c func1.c -o prg1
gcc prg2.c func2.c -o prg2
gcc prg3.c func1.c func2.c -o prg3
The results are the same, at least as long as you had linked statically to the library.
But even with a shared (dynamic) library the approach will be the same. Shared libraries "only" save some RAM if several programs using them are run concurrently. If only one program runs at a time, a dynamically linked program might need more RAM and loads slower.

Makefile for C code

I inherited a code which has a makefile, but so far I was unable to run it on a linux server. The main complain of the compiler is that it is unable to load libgmp.so.3 : error while loading shared libraries: libgmp.so.3. I know that libgmp.so.10 exists on this server, but I was wondering which part of the makefile needs to be changed so the compiler looks for libgmp.so.10 rather than libgmp.so.3.
OPTFLAG = -O2 -Wall -fPIC -fexceptions -DNDEBUG
LDFLAGS = -O2 -Wl,-no_compact_unwind -DNDEBUG -lm -pthread
COMPILER = gcc ${OPTFLAG}
LINKER = gcc ${LDFLAGS}
# CPLEX directory
CPLEX_HOME = /opt/ibm/ILOG/CPLEX_Studio1263/cplex
CPLEX_INC = ${CPLEX_HOME}/include/
CPLEX_LIB = ${CPLEX_HOME}/lib/x86-64_linux/static_pic/ -lcplex
# Compile the main file
code: code.c
${COMPILER} -c code.c -o code.o -I${CPLEX_INC}
${LINKER} -o code code.o -L${CPLEX_LIB}
clean::
rm -f *.o
rm -f ${LIB}/*.o
rm -f *~
rm -f ${SRC}/*~ ${INCLUDE}/*~
You need to rebuild whatever program or library uses libgmp.so.3 from source code. Could you provide the exact command run by make and the error message it produces?
EDIT The problem here is that the system has installed a version of the IBM CPLEX software which comes with its own GCC binary, and that GCC binary uses libgmp.so.3. The easiest way to fix this would be to upgrade the CPLEX software to a version which supports the operating system being used, or use the software on the operating system for which it was written (i.e., something really old that actually ships libgmp.so.3).
The most easy way it to install libgmp-dev package, from your linux distribution. GMP is a package library to do multiple precision calculations on large integers, which is probably needed by your program. As you put in some comments, adding -L/usr/lib64/libgmp.so.10 is an error, as -L option allows to add a directory to search for libraries, and not a specific library.
If only the library is needed and no header file is missing in your compilation (this is something strange, but sometimes happen) then you can still link with only the libgmp.so.10 object, but you have to do in a something nasty way. Just add /usr/lib64/libgmp.so.10 as an object file (not a library, with -l option) to your link command.
EDIT
From looking more closely your Makefile I see no reference to the libgmp.so.3 library, so I only can assume this is a indirect reference from some other already compiled library that comes from outside with your package. Just use
ldd lib<nameOfLibrary>.so.x.x
with all the libraries needed by your final executable, so see which shared objetc is the one that requests libgmp.so.3 soname, and then recompile it, reinstall it, or use your system's libraries ONLY, and not mesh anymore with libraries coming from another system. For example you can try (this is an expensive command, but it will get the answer)
find / -name "lib*.so.*" -print | xargs ldd > all_libs.lddout
and then find all_libs.lddout to see which library uses libgmp.so.3 (this will be the outdated library) You'll need to deinstall it or upgrade it, to be able to continue.
Linux systems have a library version system that allows an executable to be able to load different versions of the same library and allow them to live together in the same system. One of two: or you are able to locate the sources of version 3 of the shared libgmp.so.3 library and install it on your system, or you'll need to update the libraries your program uses to be able to link with the libgmp.so.10 already installed on your system.
2ND EDIT
As I see in the comments, you have changed the default compiler on your system by another coming possibly from other linux distribution (as your installed library is libgmp.so.10 while the one cc1 requests is libgmp.so.3, which is not installed on your system.
Installing a different compiler from the one you have installed, and doing that without previously deinstalling the other compiler, can lead you to this kind of problems.
The most reliable thing you can do is to reinstall the compiler from your distribution, or better, reinstall the whole linux system, as you have probably broken many things that will be emerging as you use your system. There's very poor info on what you have done to go further in your problem. Anyway, my recommendation is to not use the comment parts to add new information about your problem, just edit your question and add all those new information to it.

Questions about how libraries work in C

I am a newbie student learning C and wish to use the gLib library functions for a project: http://www.linuxfromscratch.org/blfs/view/svn/general/glib2.html
(I am using Ubuntu)
I have a couple questions about how libraries work in C and what happens when you install one or want to use one:
When I install this (run ./configure && make && make install inside the folder), what exactly is it doing? From what I learned there are shared libraries in C and static libraries in C. Is it true that it is installing library and include files to /usr/lib/ or somewhere?
When using gcc with external libraries, you have to specify -L and -I flags to specify where to look for library and header files. When I install glib, will I need to specify these flags?
If I want to package my executable for another machine, what would happen if the other machine doesn't have glib? I think if I had static libraries I would be able to include it in the binary, but how would it work for glib?
I am familiar with developing with GTK+ and GLIB. As i'm aware library files reside in /usr/lib and include files are found in /usr/include. Some libraries might be in places such as /usr/local/lib. I will attempt to answer your questions as best as I could.
When installing a library through the source package yes it installs files to the various folders /usr/share /usr/lib /usr/include and etc. It's highly recommended you use your distribution's package manager to install library packages and development headers. Installing from source is always bound to fail as necessary dependencies might be required.
This is where tools such as autogen and makefiles come handy. You don't necessarily need to concern yourself with specifying all that. tools such as pkg-config handle all that work. Most libraries will install a package configuration file into /usr/lib/pkgconfig & /usr/share/pkgconfig directories. This helps anyone developing an application easily link their code to the libraries.
Using package config to get the config:
$ pkg-config --cflags --libs glib-2.0
-I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -lglib-2.0
Linking using GCC & package config:
$gcc example.c `pkg-config --cflags --libs gtk+2.0 glib-2.0` -o example
The above command would link my program with gtk & glib.
Using Makefile to not ever have to enter those long lines again:
Makefile:
OBJS = main.o callbacks.o
CFLAGS = `pkg-config --cflags --libs gtk+-2.0`
program: $(OBJS)
gcc -o program $(OBJS)
main.o: main.c
gcc -c main.c $(FLAGS)
callbacks.o: callbacks.c callbacks.h
gcc -c callbacks.c $(FLAGS)
.PHONY : clean
clean:
rm *.o
rm program
.PHONY : install
install:
cp program /usr/bin
.PHONY : uninstall
uninstall:
rm /usr/bin/program
The above makefile is for a simple GTK+2.0 application as you can tell by what package config is including in CFLAGS to make the program executable all you have to enter in your source directory would be make. pkg-config will only work if you have installed the development packages for the library you are trying to work with. For ubuntu to install GTK+-3.0 and GLIB development files you would enter:
$ apt-get install libgtk-3-dev
I think this is a good concern for portability. No single static library is going to be cross platform. It would have to be compiled for those platforms manually. I reckon to get rid of all the headache you would use Anjuta IDE developed by the GNOME software foundation. It makes developing GLIB & GTK+ apps a breeze supporting both C & C++. It will create the Makefile, configure and other files to make developing code on cross platforms easy and make deployment easy. I could link you some resources, but my reputation on stack overflow is less then 10. So I will just mention the name of some resources below.
Further Reading
Makefile Tutorial
Anjuta IDE (C/C++)): ://anjuta.org/
GTK+-3.0 Hello World with Compiling and linking using pkg-config:
When I install this (run ./configure && make && make install inside
the folder), what exactly is it doing? From what I learned there are
shared libraries in C and static libraries in C. Is it true that it is
installing library and include files to /usr/lib/ or somewhere?
Well it is running first ./configure and then if that succeeds it runs make and if that succeeds it runs make install. configure is a script that takes care of a lot of compatibility issues between systems. They are usually shell scripts as this is the common denominator across systems so the configure script will work across various systems. One of the thing configure does is create a Makefile. The second command make will use the newly created Makefile to build the library or executable. Since you did not specify a target (like you will in the make install) make will build the default target, which is typically the all target. This is just by convention. Makefiles are basically a list of things to build (targets) along with what they depend on (dependencies) and how to build to target (rules). Finally, make install will actually install the necessary components. For libraries this is the library and necessary header files for executables it is just the program. man pages might also be installed. Where you install the libraries depends on where you specify to install them. Typically configure will take the --prefix argument that lets you control where they are installed. If you do not use --prefix you will most likely install in the default location for your system.
When using gcc with external libraries, you have to specify -L and -I
flags to specify where to look for library and header files. When I
install glib, will I need to specify these flags?
Your question is a little unclear, so let me first make sure I understand. Are you asking if after you install glib will you need to use -L and -I to tell gcc where to look for them? If so it depends on where you install them. Typically when you make and install a library you will install the library and header files in the default location or not. If you did then assuming your gcc is configured correctly then no you will not. If you did not then you will most likely have to use -L and -I
If I want to package my executable for another machine, what would
happen if the other machine doesn't have glib? I think if I had static
libraries I would be able to include it in the binary, but how would
it work for glib?
If it doesn't have glib and you used the shared libraries your application will not work. You will need to either have the same version glib libraries on the other machine or build the libraries statically. How to build them statistically depends on the library. This SO question might help.
Regarding configure, make and make install. configure is a shell script that is used to discover (and configure) your development environment. make and make install are convenient way of building your software. Where make would normally involve compiling and linking, where as make install would normally involve copying executables and libraries to standard path and setting up things (also include files if any usually in /usr/include), so that you don't have to explicitly give path before running the executable. What make does can be done by hand, but it's very cumbersome.
For glibc - yes you have to specify those flags. Normally all libraries will come in two flavors on most of the platforms. The binary form are used for dynamic linking when programs are actually loaded. Also - most distributions will have -dev or -devel versions of those libraries. Those are required for building software that makes use of those libraries (configure above can help find out whether devel libraries are installed). Typically when you see a library installed but not it's devel - you are likely to see configure errors. In short you require devel versions if you want to link with those libraries. This step is not needed if you are building libraries also from source using make and make install.
If you want to package your executable for another machine and you are not sure whether another glib is there or you want to be sure that the glib to be installed should be one specific version that you want, you should statically link while building (compiling/linking) the library. this gcc man page has got several details about link options. I believe there should be a way to statically link glib(or glib2). Though normally that may not be required if you have enough applications that are using it already.

Cross Compiling from XCode on the Mac to Windows

So, as far as I can tell, there wasn't another issue for this that was recent enough to be compatible with new major updates.
I am programing on a Mac using XCode 4.
I need to write a program for Windows.
How would I do this, and is it the same exact code just compiled differently?!?
Any and all help would be appreciated, I have no idea what I'm doing here.
Thanks!
If you stick strictly to the subset of POSIX C supported by both Mac and Windows (which implicitly limits you to console applications), you can possibly get a Windows cross-compiler for OS X and build applications that way.
However, you will likely find your life vastly simplified if you just run Windows in a VM (or via Boot Camp) and develop your application in Visual Studio or any other Windows development environment. This is the only way to make a Windows GUI app, and realistically the only good way to write a Windows console app.
While the systems are very different, it's possible to write code that compiles on both, just not any code, you'll need to design it carefully; this is called "portable code".
Say you already have your code written; then you can use MINGW (e.g. brew install mingw-w64) for cross-compiling for Windows, you can even test the resulting .exe on Mac OS X using WINE; also clang can be used to compile for Windows, but I never tried that.
Here's an example Makefile from a real project:
toolchain = mingw
#toolchain = wine
mingw- = i586-mingw32msvc-
mingw-cc := ${mingw-}gcc
mingw-cxx := ${mingw-}c++
mingw-rc := ${mingw-}windres
wine-cc = winegcc
wine-cxx = wineg++
wine-rc = wrc
CC := ${${toolchain}-cc}
CXX := ${${toolchain}-cxx}
RC := ${${toolchain}-rc}
LDFLAGS = -mwindows -mno-cygwin
LIBS = -lodbc32 -lole32 -loleaut32 -lwinspool -luuid
app-objs = app.o \
app_resources.o \
input.o \
user_manual.o \
hints.o
all: app.exe
clean:
${RM} ${app-objs} app.exe
app.exe: ${app-objs}
${CC} ${LDFLAGS} -o $# ${app-objs} ${LIBS}
.c.o:
${CC} -c ${CFLAGS} -o $# $<
.rc.o:
${RC} ${RCFLAGS} -o $# $<
.SUFFIXES: .rc .res
.PHONY: all clean
BTW, the example comes from a project originally built with MS Visual Studio by someone else, then years later ported by me to MINGW and cross-compiled from Debian GNU/Linux (but would be identical for Mac OS X). It's just an example, but most languages do have some way to cross-compile which is more or less equivalent to that.
Now, the really tricky part is the code base, a way to develop an application natively in your Mac and have it also compile for Windows is to use a well-supported portable framework, like Qt, which guarantees you the same API across many OSes.
Still, some bits about the target platforms might be slightly different and may need to be corrected, but tend to be minor changes.
So, in short, by using portable frameworks, it becomes just a matter of setting up your existing build system, whatever it is, to use the right cross-compiling tools, providing said frameworks, and it will compile more or less in the same way as it does for a native build.

Should I use automake/autoconf for distribution of a small ansi C app?

I have a small ANSI C application which compiles cleanly under different tested compilers and platforms. It doesn't use any preprocessor switches or external dependencies and the makefile is simply something like:
myapp: *.c
gcc *.c -Wall -o myapp
If I want to distribute this project in source form as portable as possible, should I wrap it using automake/autoconf? Will this actually increase portability or is it as portable as it gets as it is?
The only thing I can think is that it will automatically select the systems compiler but it will also add a lot of complexity. Is it worth it?
I doubt it's worth it. ANSI C without any OS-specific calls should be supported on every platform that has a working C compiler and adding automake/autoconf to this makes maintenance probably less pleasant than currently.
You can, however, use the $(CC) variable in your makefile to automatically use the system's compiler:
myapp: *.c
$(CC) *.c $(CFLAGS) -o myapp
You don't need to specify a compilation rule, thus there is no need in $(CC), $(CFLAGS) and $(LDFLAGS), because make has an implicit rule to make an executable from C source.
Keep your Makefile simple:
all: myapp
myapp: *.c
clean:
rm -f myapp
.PHONY: all clean
BTW it would be better to specify a list of source files because there is no guarantee that your sources will be the only C files in that directory
Although you may feel you'll gain little in terms of functionality, you may still wish to consider it for these reasons:
Using autoconf is a very common way of distributing C code. Consequently, most people should be comfortable with building and installing using that, which makes it easier for the user.
Once you've put in the effort of getting autoconf working with your project, it will make it substantially easier if and when you do want to make use of some of its additional functionality.

Resources