COSC345 2013 Software Engineering

Lecture 20: Portability Outline • Portability • Kinds of portability • portability – Abstraction layers • Standards • Tools • Data • Do we really want to port everything? • Can we port everything? Portability • Portability: – A software component is portable if it is much easier to modify it for a new environment than to rewrite anew. • Portability is cost efficient – Increase customer base – Increase revenues from existing customer base – Future proof • E.g., PowerPC to Intel on • Also – Increases software reliability – Even if you don't change platform, platforms change Kinds of Portability • Portability of anything – Early computers were hand-made • Software was custom built for each computer – Modern PCs are mass-produced • Software is written in high level languages • Portability of – Early operating systems were hard coded for the hardware • Software for the Apple ][ didn’t work on the Apple /// – Modern operating systems are backwards compatible • Software written for Windows NT works on Windows XP • But undocumented interfaces do go away or change – Common binary interfaces • Software for runs on the LynxOS RTOS Kinds of Portability • Multi-format ("fat") binaries (MacOS, Solaris) – 680x0 / PowerPC fat-binary – PowerPC / Intel universal-binary – {SPARC,Intel}x{32-bit,64-bit} in Solaris • Multi- support – Mac OS 9 to OS X compatibility; "DOS" box • Portability of microcode / virtual machine – UCSD P-system – Java write once run anywhere (oh yeah?) • Portability of source code – C standards ('89, '99, '11) – POSIX standards – Carbon APIs on Macintosh Kinds of Portability • – Exactly emulate old hardware • Apple //e emulators • Hercules IBM/360 • Simulators – Create a simulation environment for (often) new hardware • 68HC11 emulator • Cell emulator – Used for software development when hardware not present – Used when development cycle is awkward • Gameboy development environment – Can be a combination of hardware and software • In-circuit emulators • Games machine development environments Source Code Portability • If the customer base is split across different platforms – Write the same program for each – Write one program and port it to each • One code base is – More reliable, more stable, cheaper • Either way – Software must be tested (and typically debugged) on each platform – All non-portable code must be re-engineered Source Code Portability • Writing one piece of code that will – Compile on different • Future proof against disaster by a vendor – GNU / Microsoft / Borland • Increases reliability – Compile on different operating systems • Future proof against customer base changes • Increase customer base • Increases reliability – Compile for different CPUs • Future proof against OS supplier (Apple 680x0 / PowerPC / Pentium) • Increases customer base (desktop and mobile, for example) • Increases reliability Abstraction Layers • Bring all non-portable code to one place in the program • Build a common interface abstraction layer for the code • Implement the abstraction layer on each platform • Build the application on the abstraction layer

Portable Application

Common Abstraction Interface

Windows Linux Abstraction Abstraction Layer Layer

Windows Linux Abstraction Layer For File I/O class NCBI_file { protected: NCBI_file_internals *internals;

public: NCBI_file(char const *filename, NCBI_file_mode mode); virtual ~NCBI_file();

long open(void); long close(void); long create(void);

inline long seek(NCBI_file_pos origin, long64 distance); inline long64 tell(void); long write(char const *buffer, long size, long count = 1); long read( char *buffer, long size, long count = 1);

long64 length(void); char const *filename(void); NCBI_file_mode mode(void); };

Opening a File long NCBI_file::open(void) { #ifdef NT_FILES if (internals->mode_as_passed == READ_ONLY) internals->fp = CreateFile(internals->filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY | FILE_FLAG_RANDOM_ACCESS, NULL); else internals->fp = CreateFile(internals->filename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL); return internals->fp != INVALID_HANDLE_VALUE; #else internals->fp = fopen64(internals->filename, internals->mode); return internals->fp != NULL; #endif } Re-use • Using an abstraction layer – Maximizes code re-use – Forces a team to use a modular design approach • Often use several different abstraction layers – POSIX on Windows on HAL Standards

• There are many compiler / operating system / hardware “standards” • Language – C89, C99, Fortran 77, Fortran 95, ISO Pascal, COBOL 2002, etc. • Operating systems – POSIX • Hardware – 32-bit and 64-bit CPUs – IEEE floating point (binary standard; decimal standard)

• Must be supported by both the platform and the developers • Standards take time to implement – They are “just one more platform” to the developer – They are often implemented incrementally Developing Portable Software

• Choose the destinations before you start – If you're targeting a desk-top PC and an 8-bit microcontroller then you need to build suitable abstraction layers and work within the limits of both • Choose and implement the build method – Use Make if available • If not then use scripts • If not then use an IDE's build facilities – Build a common build system • If version control is available – Use it! If not, hold sources on a platform where it is. – Build on each platform nightly (port little and often) • Fix bugs as soon as they appear • Incompatibilities “slip in” without developers noticing!

• Fully test and debug on each and every platform Tools That Help • Memory checkers – Purify / BoundsChecker / Valgrind / etc. • Static source code analysis tools – Lint or splint (second semester) • Source code coverage tools – Pure coverage (second semester) • Compilers – Set warnings to maximum (-O2 -Wall -Wextra...) – Use compatibility modes (C89 / C99 on GCC) • The language – Some are inherently more portable than others – Do not use complex language tricks • Keep code readable, comprehensible, and simple Things to expect • Missing “standard” functions – stricmp / strcasecmp • Functions that behave differently (or are buggy) – strnlen • Functions with different definitions – exit • Different models of the same feature – threads (big Windows/POSIX differences) • Different versions in different places – stdargs / varargs Data Portability • Characters – Different platforms have different character sets • ASCII / EBCDIC / Unicode • Integers fwrite(&my_int, sizeof (my_int), 1, fp); fread( &my_int, sizeof (my_int), 1, fp); – Might not work! • Different size of integers • Different byte order of integers

For the integer: 4A3B2C1D

100 101 102 103 100 101 102 103 4A 3B 2C 1D 1D 2C 3B 4A big-endian (SPARC) little-endian (Pentium) Endian Conversion #if defined (INTEL_BYTE_ORDER)

inline long32 internalise(long32 here) { return here; } inline long32 externalise(long32 here) { return here; }

#elif defined(SPARC_BYTE_ORDER)

inline long32 internalise(long32 val) { return internalise(&val); } inline long32 internalise(long32 const *data) { unsigned char const *from = (unsigned char const *)data; return (from[0]<<0)|(from[1]<<8)|(from[2]<<16)|(from[3]<<24); }

inline long32 externalise(long32 val) { long ans; unsigned char *to= (unsigned char *)&ans; to[0] = (unsigned char)(val>>0); to[1] = (unsigned char)(val>>8); to[2] = (unsigned char)(val>>16); to[3] = (unsigned char)(val>>24); return ans; }

#else #error "Unknown byte ordering - must be SPARC or INTEL" #endif

Use the POSIX functions htonl(), htons(), ntohl(), ntohs(). A third byte order has been used (2301) in real machines. Consider The User Base • Different user-cultures warrant different consideration – Macintosh / Windows • Different UI conventions need consideration – Macintosh / Linux (Cmd-V vs Ctrl-V, for example) – Can sometimes be solved with cross-platform GUI libraries • Different user expectationa – American / British spellings – See internationalisation and localisation in Semester 2. • Different hardware – Should we use software from Boeing on an Airbus? High level languages help • Use Lisp or Smalltalk or Erlang and – hardware is invisible – hardware word size is invisible (integers are whatever size you need them to be) – hardware floating point still shows through (although Lisp usually has bigfloats as well) – language runtime hides OS interface differences – especially threads – System may have own GUI library (e.g. Smalltalk) or use portable toolkit (e.g.,Erlang+wxWindows) – You will not have direct access to everything on the system (e.g. mmap(1) makes no sense in Erlang), so for some things “foreign” code may be needed. Sometimes We Can't Port! • Hardware / OS requirements – The Bonus Bonds machine • Had a hardware random number generator • Intel's 64-bit chips have a RdRand instruction that does hardware true random number generation; ARM use the AuthenTec SafeXcel IP-76 core; UltraSPARC T2 and up have hardware random number generators; IBM Power7+ ditto. – Real time programs • Control of a Jet fighter not applicable for control of a car – High performance software • The Cray vector processing hardware is unique (at least in detail; if you can play video games on it, it probably has some kind of vector support). • Modern high performance stuff should use MPI, or OpenMP, or OpenCL, which do port; or Fortran 95. References • Spectrum Signal Processing, How to Achieve Application Software Portability

• http://en.wikipedia.org/wiki/Porting and http:// en.wikipedia.org/wiki/Software_portability

• Brian Hook, Write Portable Code: An Introduction to Developing Software for Multiple Platforms, 2005. (Mostly C, some C++.)