LazarusCompleteGuide:5.0
대상플랫폼
라자루스 프로그램은 다음 세 가지 메인 컴퓨터 플랫폼에서 실행되도록 컴파일할 수 있다: Windows, Linux/Unix, MacOS X. 이는 프리 파스칼 컴파일러가 대상으로 하는 플랫폼보다 수가 적은데, 이유는 적절한 컴파일러 성능 조건만으로는 새 플랫폼을 위한 라자루스 프로그램을 생성하기에 충분하지 않기 때문이다.
어느 플랫폼에서든 라자루스의 설치 및 실행을 가능하게 하려면 먼저 아래 3개의 조건을 충족시켜야 한다:
- target platform 에서 현재 프리 파스칼 컴파일러를 이용할 수 있어야 한다. 현재로선 Windows 32, Windows 64, Windows CE, MacOS X, Linux, FreeBSD, OS/2 (그리고 eComStation), Solaris (특정 범위까지)에서 이용할 수 있다.
- target CPU 에서 현재 프리 파스칼 컴파일러를 이용할 수 있어야 한다. 현재로선 x86, x86_64, PowerPC, PowerPC64, Arm (ARM4-명령어세트만 해당, THUMB 명령어 집합은 지원되지 않음) 에서 이용할 수 있다.
- 운영체제 플랫폼에 GUI library 를 이용할 수 있어야 한다. 현재 완전히 지원되는 운영체제는 Windows, Windows CE, MacOS X, 그리고 Linux/Unix 위젯세트 Gtk 버전 1과 2, 플랫폼 독립적 Qt 4(Unix MacOS, Windows)가 있다.
세 번째 조건은 라자루스로 OS/2 프로그램을 생성할 수 없는 이유이기도 하다. X86-OS/2용 프리 파스칼 컴파일러가 존재하긴 하지만 OS/2 표현 관리자(presentation manager)와 eComStation에 대한 지원은 하지 않는다. 이론상으로는 XFree86/2에 라자루스를 설치하고 X에 대한 OS/2 프로그램을 생성하는 것이 가능하다. 아직 시도한 사람은 없지만 모든 조건을 만족시키는 방법도 있다: 컴파일러가 있고 Gtk 버전 2의 포트가 있다.[1]
네 가지 기본 플랫폼 Win32/64, WinCE, MacOS, Linux/FreeBSD/Solaris 간에는 큰 차이가 있다. 따라서 다수의 플랫폼에서 실행할 애플리케이션을 생성할 경우 (또는 소프트웨어를 한 플랫폼에서 다른 플랫폼에 복사할 경우) 생성하기 전에 우선 각 플랫폼에서 라자루스를 사용하는 것과 관련된 세부 내용에 익숙해지는 것이 중요하다. 그러면 어떤 문제에 직면할지 미리 조사할 수 있고, 원하는 여러 플랫폼에서 문제없이 실행시킬 수 있는 소프트웨어를 설계하면서 올바른 선택을 내릴 수 있다. 경험이 어느 정도 있다면 하나의 플랫폼에서 소프트웨어를 완전히 개발시킨 후 추가 플랫폼에 이용 가능하도록 프로그램을 단순히 재컴파일하는 방법도 있다.
이번 장에서는 이 주제에 대한 일반적인 개관을 제공하여 여러 운영체제의 주요 특징들을 설명함과 동시에, 필요 시 네이티브 API를 사용하는 방법을 소개한다 (고이식성 소프트웨어를 생성 시에는 피하는 것이 보통).
처음에는 라자루스로 개발한 프로그램이 모든 플랫폼에서 실행되도록 만들기가 매우 수월해 보일 것이다. 모든 대상 플랫폼에 존재하는 라이브러리만 사용할 경우나 프리 파스칼 RTL, FCL, 라자루스 LCL로의 호출만 사용할 경우, 사용자 프로그램은 이론적으로 어떠한 변경을 하지 않고도 지원되는 플랫폼 어디서든 작동할 것이다. 이론적 이란 말을 붙인 이유는 운영체제들 간 기억해야 할 차이점이 많기 때문이다. 모두 올바로 진행되면 RTL, FCL, LCL 은 이러한 차이들을 숨기거나 최소화시키지만 그를 둘러싼 작업은 해야 할 것이다 (필요가 있다면 코드를 수동으로 수정해야 할지도 모른다).
라자루스의 라이브러리에서 제공하지 않는 기능을 필요로 할 때 상황은 복잡해진다. 이런 경우 우리는 어쩔 수 없이 다중 플랫폼에서 (바라건대 그 모든 플랫폼에서 실행되길) 그 기능을 제공하는 다른 라이브러리를 찾거나, 각 운영체제의 저수준(low-level) API와 작업하면서 각 플랫폼에 대한 솔루션(solution)을 구현해야 할 것이다. 이러한 일이 발생한다면, conditional compilation(조건부 컴파일)을 사용해야 할 것이다.
조건부 컴파일을 할 때는 다른 운영체제들을 위해 소스 코드의 부분들을 구분할 필요가 있다. 프리 파스칼은 이를 위해 명령 시퀀스를 제공한다:
$ ifdef condition ... $else ... $endif
하지만 큰 루틴에서 운영체제마다 소스 코드를 이러한 방식으로 엮는 것은 이상적이지 못하다. 그보다는 각 플랫폼에 대한 하나의 솔루션을 제공하는 구분된 소스 파일을 제공하는 편이 더 깔끔하다. 프리 파스칼과 라자루스에서 이를 처리하기 위해선 $include filename.inc 메타 명령을 이용하여 해당 코드 부분을 소스로 포함시키는 방법이 있다. 명령 규칙은 플랫폼마다 하나의 하위디렉터리를 생성하여 모든 플랫폼 특정적 코드를 적절한 하위디렉터리로 넣는 것이다.
조건부 명령인 $ifdef 와 $endif 사이에 있는 내용이 무엇이든 $ifdef 이후에 주어지는 조건이 True일 때만 사용될 것이다. 이는 include 문에도 적용된다. 우리는 코드에 한줄 구성으로 된 운영체제 하나만 위한 파일을 포함시킬 수 있다.
{$ifdef Linux} {$include linux.inc} {$endif}
{$ifdef Win32} {$include win32.inc} {$endif} // ... etc .
조금 더 정교한 방법은 검색 경로에 매크로를 이용하는 것이다. include 파일에 대한 검색 경로에 $(TargetOS)를 포함할 경우 단순히 아래와 같이 사용하면 되겠다:
{$include unit1.inc}
그렇다면 Win32에 해당하는 include 파일은 Win32/unit1.inc 에 위치하고, 리눅스에 해당하는 include 파일은 Linux/unit1.inc 에서 찾을 수 있을 것이다. 이렇게 하나의 디렉터리에 모든 리눅스 특정적 파일을 보관할 수 있다. 매크로는 유닛 검색 경로에 사용해도 좋다.
플랫폼 의존적인 소스 코드를 함수로 조직하고 메소드를 구조적인 방식으로 구분된 파일에 조직한다면 루틴의 재사용을 보장할 수 있다. $ifdef 명령도 중첩할 수 있다. 컴파일러 스위치(compiler switch)와 조건부 컴파일에 관한 상세 정보는 Michael Van Cannëyt의 저서 Free Pascal 2 Hanbook 의 5.3장, 732페이지부터 찾아볼 수 있다.
가장 중요한 컴파일러 정의는 표 5.1과 (컴파일러와 CPU 특정적) 표 5.2에 (운영체제 특정적) 실려 있다.
정의 | 설명 |
FPC | 현재 컴파일러가 프리 파스칼이다 |
VER2 | 프리 파스칼, 버전 2.x.x |
VER2_0 | 프리 파스칼, 버전 2.0.x |
VER2_2 | 프리 파스칼, 버전 2.2.x |
VER2_4 | 프리 파스칼, 버전 2.4.x |
ENDIAN_LITTLE | 대상 프로세서가 little endian이다 (x68, Alpha, ARM) |
ENDIAN_BIG | 대상 프로세서가 big endian이다 (PowerPC) |
CPUI386, CPU386, CPU86, CPU87 | 대상 프로세서가 x86 호환 가능하다 |
CPUPOWERPC | 대상 프로세서가 PowerPC이다 |
CPUARM | 대상 프로세서가 ARM이다 |
CPUX86_64 | 대상 프로세서가 ARM64 또는 Intel 64 비트이다 |
CPU32 | 대상 프로세서가 32 비트 CPU이다 |
CPU64 | 대상 프로세서가 64 비트 CPU이다 |
표 5.1: 프리 파스칼 컴파일러에 알려진 공통으로 사용되는 CPU 정의 |
프리 파스칼 및 라자루스 라이브러리는 애플리케이션 프로그램이 필요로 하는 거의 모든 기능을 지원하는 매우 강력한 라이브러리이다. 사용자는 프로젝트마다 플랫폼의 저수준(low-level) 루틴을 사용할 필요가 있는지 주의 깊게 고려해야 한다. 당연히 조심해서 선택해야 하며, 특히 최신 운영체제 버전의 GUI와 관련해서 더 주의해야 한다.
새로 도입된 장치(gimmick)를 모두 다른 플랫폼으로 전달하는 것은 실현 가능성이 없다. 그리고 플랫폼 특정적 도움말 기능의 사용도 피하도록 한다. 한 플랫폼에 플랫폼 특정적 도움말을 포함시키면 다른 플랫폼에서도 어쩔 수 없이 사용자 스스로 (가능하다면) 구현해야 할 것이다.
정의 | 운영체제 |
LINUX, UNIX | Linux |
FREEBSD, BSD, UNIX | FreeBSD |
SUNOS, SOLARIS, UNIX | Solaris |
WIN32, MSWINDOWS, WINDOWS | 32-bit Windows |
WIN64, MSWINDOWS, WINDOWS | 64-bit Windows |
WINCE, WINDOWS | Windows CE, Windows Mobile 등 |
BSD, DARWIN, UNIX | MacOS X |
표 5.2: FPC로 이용하는 가장 자주 사용되는 대상 운영 체제 정의 |
아래의 부분적 리스트는 자신의 소스 코드에서 이러한 컴파일러 정의 중 일부를 어떻게 사용하는지 보여준다. 해당 코드 섹션은 윈도우와 유닉스용으로 구현되었으며, 현재 사용자의 로긴 이름을 요청한다. 소스 코드는 $ifdef 지시어를 이용한다. 플랫폼 의존적 코드는 재사용 가능한 함수들 내부에 따로 구분되어 있다. uses 절 또한 ifdefs 를 이용해 윈도우에서 Windows 유닛을 참조한다는 점을 주목하라. 플랫폼 의존적 코드를 문서화할 때는 특히 그것이 필요한 이유를 설명함과 동시 주의 깊게 문서화할 것을 항상 명심한다. 사실상 이것은 매우 중요한데, 본 저작자가 아닌 프로그래머들이 복사(porting)를 하는 일도 종종 있을 것이며, 운영체제의 새 버전이 추후 예기치 못한 방향으로 규칙을 변경할지도 모르기 때문이다.
uses SysUtils, {$ifdef Windows}Windows{$endif}
{$ifdef UNIX}LCLProc{$endif} ;
{ function GetCurrentUserName: String;
Returns the current user name.
The resulting string is utf-8 encoded.
Supported platforms: Windows NT, WinCE, UNIX }
function GetCurrentUserName: String;
{$ifdef Windows}
var
WideBuffer: array[0..255] of WideChar;
BufferSize: Cardinal;
begin
BufferSize := 256;
Windows.GetUserNameW(WideBuffer, BufferSize);
Result := UTF8Encode(WideBuffer);
{$endif}
{$ifdef UNIX}
var
Buffer: String;
begin
Buffer := GetEnvironmentVariableUTF8('USERNAME');
if Buffer = '' then
Buffer := GetEnvironmentVariableUTF8('USER');
Result := Buffer;
{$endif}
end;
Notes
- ↑ 사실 지금은 Gtk만이 아니라 QT4도 있기때문에 가능성은 높다고 할 수 있다.