This page is aimed at both Debian and upstream developers, talking about things to keep in mind when developing a program or packaging it so that it remains portable.
- See also
- Cross Compiling
- Best Practices
- Programming Considerations
- Feature vs System detection
- Build Failures
- Runtime Failures
http://bootstrap.debian.net/ - reports if packages' build dependencies could not be satisfied if bootstrapping a new arch
Compare built status of packages across many arches, including the lesser-known ones; provide a maintainer email address as p= parameter, for example the XFCE team's summary:
The official Debian archive is built natively so cross-building support is not vital for portability within Debian. However it may be important to other distributions. It may also be useful to speed up the process of debugging failures of large packages on slow architectures.
- The system the software is being compiled on.
- The system the software will run on.
- When building a compiler, the system the software built by the compiler will run on.
($DEB_BUILD_ARCH, $DEB_HOST_ARCH, $DEB_TARGET_ARCH & co.)
Reduce the amount of arch-specific Build-Depends by building whatever is possible (documentation for example) in a binary-indep target, hence those dependencies become Build-Depends-Indep and only one architecture needs these.
Building and Running Tools
Autoconf and Automake
(how to build a toplevel project)
ArchitectureSpecificsMemo has details on many specifics of given architectures in Debian. This page will limit itself to more general advice.
The basic C integer types ("char", "short", "int", "long", "long long" and their unsigned counterparts) can vary in size between platforms. The C standard defines minimum sizes in terms of ranges of supported values. All debian ports and most other common platforms use straight binary for unsigned types and twos complement for signed types.
"char", "signed char" and "unsigned char" are required by the C standard to be the size of a single memory location and to be at least 8 bits in size. All current Debian architectures use 8-bit memory locations making char exactly 8 bits. This is also the norm on modern architectures outside Debian. The default signdness of a plain "char" varies between ports of Debian. Code that cares about the numeric behaviour of char values outside the range 0 - 127 should explicitly use signed char or unsigned char as appropriate.
"short" and "unsigned short" are required (by implication from the required ranges of supported values) to be at least 16 bit. In practice they are exactly 16 bit on most modern platforms.
"int" and "unsigned int" must be at least are large as short/unsigned short. In practice on all Debian ports and most modern 32-bit and 64-bit platforms they are 32-bit but on obsolete and micro-controller platforms they may be smaller.
"long" and "unsigned long" must be at least as large as int/unsigned int and they are also required (by implication from the required ranges of supported values) to be at least 32 bit. On all current Debian ports they are the same size as a pointer. However this is not true on 64-bit windows.
"long long" and "unsigned long long" must be at least as large as int/unsigned int and they are also required (by implication from the required ranges of supported values) to be at least 64 bit. On all current Debian ports and in nearly all other systems they are exactly 64-bit. However they were newly introduced in C99 so using them can limit portability to old platforms.
The C99 standard introduced a header inttypes.h which provides types of fixed size and also types with a guaranteed minimum size and types with the same size as pointers. It is recommended to use the C99 types where the guaranteed ranges of the basic C types are insufficient, where a data structure will be stored to or retrived from disk/network and where a pointer must be converted to an integer. If the code needs to be ported to pre-c99 compilers the project may need to provide their own header to define the C99 types on those platforms based on platform specific knowledge.
Debian has both big endian and little endian ports. Code that reads/writes data structures from files or to the network needs to be aware of this and perform endian conversions as needed.
Endian differences can also change the behaviour if pointers are (explicitly or implicitly) converted to another type. Such conversions should be avoided but they may be encountered in existing code. For example if a pointer to long is converted to a pointer to int the value will be read correctly on 32-bit ports where int and long are the same size. They will also generally be read correctly (though strict aliasing rules mean it's not gauranteed) on 64-bit little endian ports where the pointer to int will point at the least significant part of the long. However they will not return the correct result on 64-bit big endian ports where the pointer to int will point at the most significant part of the long.
In most cases floating point types have the same endian as integer types. However the old "arm" port used mixed-endian doubles. Extra care is likely to be needed if storing/retriving floating point values.
Different platforms handle unaligned accesses in different ways and have different alignment requirements. i386 and amd64 are generally very forgiving about unaligned accesses handling them correctly in hardware in nearly all cases. Other platforms are less forgiving and may result in "bus errors", incorrect behaviour or very slow fixups. Such accessses are often usually a violation of strict aliasing rules so with a modern compiler they may give undefined behaviour on any platform. In general unaligned access should be avoided. If it is vital to access a value from a potentially unaligned location memcpy may be used to do so in a portable way.
When designing data structures whose internal structure must be the same on multiple platforms (e.g. because they will be saved to disk or sent over a network) it is best to follow "natural alignment". That is placing 2 byte fields on 2-byte alignment, 4 byte fields on 4 byte alignment and 8 byte fields on 8 byte alignment.
Floating Point Behaviour
The C standard places very little requirement on the size and behaviour of floating point types. Most modern platforms use the IEEE 754 single and double precision formats for float and double. However use of the IEEE 754 types does not necessarily imply fully IEEE 754 compliance. This is especially true if -ffast-math is in use. In general it is best to avoid relying on the exact values returned by floating point calculations.
Long double varies substantially in size and behaviour between platforms and is often no-better than double. In general it's use should be avoided in portable code. If more than double precision is required in a portable manner then a library such as gmp must be used (possiblly in combination with platform specific code using long double on platforms where it is known to be sufficient)
Feature vs System detection
System detection (usually wrong)
CPP system macros
Other system checks ...
Check for specific functionality (good)
(PATH_MAX and friends)
Optional POSIX features
(linuxisms: mount, linuxthreads abuse)
(bsdisms: sys_errlist vs. strerror())
tcphdr, udphdr struct members have different names on BSD than on Linux; compiling with -D__FAVOR_BSD was commonly used to work around this until glibc 2.19 (see 764692); it should hopefully be no longer needed any more in the near future.
devfs on kFreeBSD
(some nodes are not allowed on FreeBSD's devfs)
Missing kernel features
Hardcoded values instead of using macros
Making dirs ending with a slash '/'