삽질하는플머

소금과 후추를 위해 fpc 간만에 진지하게 가지고 놀기

Lazarus

잘 될지는 모르지만... 구글의 소금과 후추에서 FPC로 만든 바이너리를 한 번 링크해보고 싶다는 욕망!이 생겼다. 

기왕이면 최근에 발표된 2.6.0 을 가지고 놀아보자. 점심먹고 잠시 짬이나 우분투에 올려봄. 


ftp://freepascal.stack.nl/pub/fpc/dist/2.6.0/


여기서 i386-linux 를 다운받아 압축풀고 sudo ./install.sh 실행. /usr 에 설치. 

프리파스칼 IDE인 fp 도 함께 설치. 돌려보니 libtinfo.so.5 가 없다는 에러만 나온다. 


http://community.freepascal.org/bboards/message?message_id=682920&forum_id=24082


위 링크 참조. sudo ln -s /lib/libncurses.so.5 /usr/lib/libtinfo.so.5 실행. 

이제 제대로 돌아가네. 










소금과 후추를 위해서는 32비트와 64비트 환경이 모두 필요. 


32비트 우분투 서버에서 64비트용 FPC 크로스 빌드환경을 만들어보자. 

방법은 예전 i386-wince 크로스 빌드때와 별반 다를 바 없음. 


일단 fpcbuild-2.6.0.tar.gz 다운로드. 


# tar -xzf fpcbuild-2.6.0.tar.gz

# cd fpcbuild-2.6.0/fpcsrc

# cd compiler

# make cycle CPU_TARGET=x86_64 OS_TARGET=linux



생성된 ppcrossx64를 옮기고 심볼릭 링크


# sudo cp ppcrossx64 /usr/lib/fpc/2.6.0/

# ln -s /usr/lib/fpc/2.6.0/ppcrossx64 /usr/bin/ppcrossx64 



이제 RTL을 빌드해야 하는데... 어셈블러 x86-64_linux-as 와 링커 x86_64-linux-ld 가 필요하다. 
어디서 이 물건들을 구하지... 하고 한참을 고민했는데... gcc 에 -m64 라는 플래그가 있던 걸 기억해냄. 

다음 사이트를 참조. 
http://wiki.freepascal.org/Cross_compiling

/usr/bin 에서 어셈블러 스크립트 작업. 

# cd /usr/bin
# sudo vi x86_64-linux-as


#!/bin/bash
as --64 $@


마찬가지로 링커 스크립트도 만든다. 

# sudo vi x86_64-linux-ld

#!/bin/bash
ld -A elf_x86_64  $@


실행권한을 주고 

# sudo chmod +x x86_64-linux-ld
# sudo chmod +x x86_64-linux-as


RTL 빌드. 

# cd .... /rtl
# make CPU_TARGET=x86_64 OS_TARGET=linux PP=ppcrossx64 


생성된 라이브러리를 이동. 

# sudo mv units/x86_64-linux/ /usr/lib/fpc/2.6.0/units/x86_64-linux/rtl


test.pas 를 만들어보자. 

program test;

begin
  WriteLn('Hello World!');
end.


64비트로 컴파일. 빌드 과정을 구경하기 위해 실행파일이 아닌 어셈블러와 링크 명령이 나오게 하자. 

# ppcrossx64 -a -s -vbr test.pas


생성된 ppas.sh 를 돌려주면 실행파일 test가 생긴다. 
이 스크립트의 속을 들여다보면 앞에서 만든 어셈블러와 링커 스크립트 대신 as, ld 를 직접 호출하고 있네. 흠냐... 재미있군. 



아무래도 소금을 위해서는... 이 RTL들이 후추의 툴체인으로 재컴파일되어야 하는 것 아닌가 싶은데... 







C 에서 파스칼로 만든 코드 호출 테스트. 



먼저  SumTest() 함수를 정의한 pas_unit.pas 파일 작성


{$ifdef FPC}

  {$mode delphi}

{$endif}


unit pas_unit;


interface


function SumTest(a, b: Integer): Integer; cdecl; export;


implementation


function SumTest(a, b: Integer): Integer;

begin

  Result := a + b;

end;


end.



메인 프로그램은 C로 만든다. c_main.c 작성. 

#include <stdio.h>
#include <stdlib.h>

#ifdef __cplusplus
extern "C" {
#endif

int SumTest(int a, int b);

#ifdef __cplusplus
}
#endif

int main() {
    int a = SumTest(3,5);

    printf("Hello world! %d \n", a);
    return 0;
}


파스칼 코드부터 컴파일. 

# fpc pas_unit.pas 


C코드 컴파일. -c 옵션을 주어 컴파일 까지만 진행한다. 

# gcc -c main.c


생성된 오브젝트 파일들을 버무려 링크. 

# gcc -o test c_main.o pas_unit.o


만들어진 실행파일 test 를 돌려보면 "Hello world! 8" 이라고 출력됨. 
파스칼에서 만든 SumTest 함수가 제대로 호출되고 있다. 이 상태 까지는 후추의 툴체인으로도 빌드 가능


단순한 함수가 아닌, 파스칼의 RTL을 호출하는 경우는 어떨까?? pas_unit.pas 를 다음과 같이 수정. 

........

function SumTest(a, b: Integer): Integer; cdecl; export;
procedure PrintTest(); cdecl; export;

implementation

........

procedure PrintTest();
begin
  WriteLn('Hello world! from Pascal!!');
end;

end.


C 코드도 다음과 같이 수정. 

..........

int SumTest(int a, int b);
void PrintTest();

int main() {

   ..........
    PrintTest();
    return 0;
}


각각 컴파일 후 링크 시도. 

# gcc -o test c_main.o pas_unit.o


파스칼 쪽에서 링크 에러가 주르륵 발생한다. 예전 아이폰 SDK 삽질의 기억을 되살려 다음과 같이 시도. 

# upath=/usr/lib/fpc/2.6.0/units/i386-linux
# ar -q ./libfpc.a `ls "$upath"/*/*.o | grep -v 'linux/fv/'`
# ranlib libfpc.c
# gcc -o test c_test.o pas_unit.o -L. -lfpc


잘 안되는군... si_*.o 에서 "_start" 함수가 중복된다고 빽빽거림. 

위에서 사용했던 a -s -vbr 옵션으로 파스칼 프로그램을 하나 컴파일. 생성된 링크 스크립트 "link.res" 를 살펴보자. 
INPUT() 섹션에 system.o 와 함께 si_prc.o 가 포함된다. 
이 링크 스크립트를 복사해 INPUT 섹션을 수정하고 si_prc.o 는 지운 뒤 링크 시도. 

.......
INPUT(
c_main.o
pas_unit.o
/usr/lib/fpc/2.6.0/units/i386-linux/rtl/system.o
)
.......


# ld -o test link.res 


이번에는 system.o  내의 함수들이 링크되지 않는군... 쩝쩝... 






2013.1.23. 추가. 


결론부터 말해... 파스칼에서 라이브러리로 정의해 출력된 오브젝트 파일은 문제없이 링크 가능하다. 


다음과 같은 파스칼 라이브러리를 만들고


testl.pas


library testl;


procedure print_test; cdecl; export;

begin

  WriteLn('Hello world from Pascal');

end;


function sum_test(a: Integer; b: Integer): Integer; cdecl; export;

begin

  sum_test := a + b;

end;


end.




fpc로 빌드하면 libtestl.so 가 나온다. 얘는 안쓸꺼니까 버리고, testl.o 파일만 잘 챙겨두자. 


C코드는 다음과 같이. 

testc.c

include <stdio.h>
#include <stdlib.h>

#ifdef __cplusplus
extern "C" {
#endif

int sum_test(int a, int b);
void print_test();

#ifdef __cplusplus
}
#endif

int main() {
    int a = sum_test(3,5);

    printf("Hello world! sum_test(3,5) -> %d \n", a);
    print_test();
    return 0;
}



-c 옵션으로 컴파일만 되게 하고

gcc -c testc.c


앞에서 챙겨둔 testl.o, libfpc.a 와 링크. 

gcc -o test testc.o testl.o -L. -lfpc


실행시켜보면 잘 동작한다. 



글 시작은 우분투였는데 화면이 맥인건... 그 사이에 삽질용 맥이 생겼기 때문... 잇힝~

굳이 이렇게 C 기반위에 파스칼을 붙이는 키메라를 깨작거려보는건,
안드로이드에서 FPC를 가지고 놀다가 -XX, -CX가 거의 무용지물이길래 혹시나 싶어 다시 꺼내들어 본건데...
필요한 함수가 있으면 귀찮은 헤더변환따위도 필요없이 C로 만든 메인에 구현하고 바로 빨아먹는 장점도 있고...

하지만 결과물의 크기는 FPC의 최대 단점. FPC의 XCode용 iOS 템플릿은 이렇게까지 커지지 않았는데...
뭔가 쌈박한 링크옵션이 있는건가...

라자루스의 LCL 폼에 ActiveX 객체 올리기.

Lazarus

간만에 라자루스 관련 포스팅. 

저녁 먹고 라자루스 포럼을 뒤져보다 재미난 주제를 발견. 
http://www.lazarus.freepascal.org/index.php/topic,9311.0.html 

윈도에서 라자루스 LCL폼 위에 ActiveX 컨트롤을 올릴 수 있느냐는 질문. 오~!!
라자루스의 포터블한 특성이 사라지네 어쩌네 이런 저런 갑론을박이 있었지만,
어쨌건 글타래 말미에 ATL.DLL 을 사용한 멋진 해법이 올라와있다. 
빈 LCL폼 위에 TPanel을 올리고 다음과 같이 코딩. 


......

implementation


......

uses
  Windows, ComObj;

type
  PIUnknown=^IUnknown;
  TAtlAxAttachControl = function(Control:IUnknown; hwind:hwnd;ppUnkContainer:PIUnknown): HRESULT; stdcall;

........

constructor TForm1.Create(aOwner: TComponent);
var
  atl: hModule;
  AtlAxAttachControl:TAtlAxAttachControl;
  WebBrower:variant;
begin
  inherited Create(aOwner);
  atl := LoadLibrary('atl.dll');
  AtlAxAttachControl := TAtlAxAttachControl(GetProcAddress(atl, 'AtlAxAttachControl'));

  WebBrower:= CreateOleObject('Shell.Explorer');
  AtlAxAttachControl(WebBrower, Panel1.Handle, nil);
  WebBrower.Navigate('http://oranke.tistory.com');
end;




실행시켜본 결과는 짜잔~~


그 밑에 딸려있는 idl2pas 에 대한 링크도 재미나 보인다. 
http://www.nohl.eu/tech-resources/using-typelibs-with-freepascal/ 

다음에 시간날 때 뒤져봐야지... 


달력 컨트롤

Lazarus/KOL-CE
라자루스를 어디에 쓸까 고민하다가... 음력에 대한 공부도 할 겸 "달 구경" 프로그램을 짬짬이 만들고 있다.
날짜 선택을 위해 달력 컨트롤을 올려놓으려고 했더니... 응? 델파이의 TDateTimePicker에 해당하는 TKOLDateTimePicker 는 있는데 TMonthCalendar에 해당하는 컨트롤이 없다. 허걱~~ fpc의 wince 유니트를 열어보니 DateTimePicker와 MonthCal에 사용되는 ICC_DATE_CLASSES 가 주석으로 막혀있네~ (라자루스 0.9.24 의 LCL 윈CE 인터페이스는 현재 베타버전이다.)

그렇다면 KOL의 TKOLDateTimePicker 는 뭘까 싶어 다시 KOL.pas 를 뒤져보니... 여기에 다시 재정의 되어있었다. 그러나 애석하게도 달력 컨트롤에 대한 코드는 찾아볼 수 없었다.

처음에는 간단히 LCL로 DLL을 만들어 쓰려고 했었는데... 위에서 언급했듯이 아직 윈CE 인터페이스가 베타라 arm-wince 로 달력 컨트롤을 컴파일하면 에러가 발생한다. 뭐 여차저차해서 만들기로 했고, 아직 내공부족으로 KOL 컴포넌트화 할 수는 없었지만 그럭저럭 쓸만하기에 방법을 정리 해 본다.

달력 컨트롤에 관련된 상수들은 evc와 함께 설치한 STANDARDSDK 5.0 에 정의되어 있으며 "C:\Program Files\Windows CE Tools\wce500\STANDARDSDK_500\Include\...\commctrl.h" 에서 찾을 수 있다. 이 파일을 번역하려고 슬쩍 훑어보니 다행히 상수 및 구조체가 윈도 32와 99% 동일하다. 그렇다면?? 델파이의 VCL을 가져와 쓰자.


VCL은 오픈소스가 아니므로 여기에 옮길 수 없다. 델파이 5.0을 기준으로 과정만 소개 해 본다.


1. 먼저 라자루스의 File->NewUnit 를 선택, 새로 만들 달력 컨트롤이 담길 유니트를 만들어 준다. 이름은 적당히.

2. uses 섹션에는 Windows, Messages, KOL, CommCtrl 유니트를 적어넣는다.

3. ...\Delphi5\source\rtl\win\commctrl.pas 파일을 열고 다음 내용들을 복사 해 온다.

3-1. 6371번 줄 (MONTHCAL CONTROL) 부터 6664번 줄 (MonthCal_GetUnicodeFormat) 까지 interface 섹션에 복사 해 넣는다.

3-2. 8508번 줄 (MonthCal_GetCurSel 함수) 부터 8623번 줄 까지 implementation 섹션에 복사 해 넣는다.

3-3. MonthCal_SetUnicodeFormat, MonthCal_GetUnicodeFormat 함수는 wince에서는 동작하지 않는다. 이 두 함수의 선언 및 구현부는 {$ifdef win32} 지시자로 감싸둔다.

4. KOL의 TControl 기반의 달력 컨트롤을 생성할 수 있도록 다음과 같이 NewMonthCalendar 함수를 만들어 준다.

function WndProcMonthCalendarNotify( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
begin
  Result := false;
end;

function NewMonthCalendar( AParent: PControl): PControl;
const
  CS_OFF = {$ifdef win32}CS_OWNDC or CS_CLASSDC or {$endif}CS_PARENTDC or CS_GLOBALCLASS or
           CS_VREDRAW or CS_HREDRAW;
begin
  DoInitCommonControls( ICC_DATE_CLASSES );
  Result := _NewCommonControl( AParent, MONTHCAL_CLASS,
         WS_CHILD or WS_VISIBLE or WS_TABSTOP,
         TRUE,  nil );
  Result.SetSize( 191, 154 );
  Result.AttachProc( WndProcMonthCalendarNotify );
{$ifdef wince}
  Result.Perform(CCM_SETVERSION, COMCTL32_VERSION, 0);
{$endif wince}
end;



작업은 이것으로 끝. 달력을 생성해 폼 위에 올리는 코드는 다음과 같다.

TCalendarForm = ....
...
private
  fCalendar: PControl;
...

procedure TCalendarForm.KOLForm1FormCreate(Sender: PObj);
begin
  fCalendar := NewMonthCalendar(Form);
end;



달력에 날짜를 지정하거나 읽어올 때는 델파이에서 복사해 온 MonthCal_SetCurSel, MonthCal_GetCurSel 를 쓰면 편하다. 한가지, KOL의 SystemTime2DateTime은 델파이와 달리 1899년12월30일이 아닌 0년 0월 0일을 기준으로 한다. 아직 분석도 다 안끝났고 무엇보다 귀찮으니 그냥 SysUtils에 정의된 SystemTimeToDateTime() 을 쓰자.


function TCalendarForm.GetDateTime: TDateTime;
var
  pst: TSystemTime;
begin
  MonthCal_GetCurSel(fCalendar.Handle, pst);
  Result := SysUtils.SystemTimeToDateTime(pst);
end;

procedure TCalendarForm.SetDateTime(const AValue: TDateTime);
var
  pst: TSystemTime;
begin
  SysUtils.DateTimeToSystemTime(AValue, pst);
  MonthCal_SetCurSel(fCalendar.Handle, pst);
end;




폰트 크기를 50으로 주고 480*800 에뮬레이터에서 돌려본 화면은 짜잔~~ 멋지지 않은가!!







3월 23일 릴리즈 된 0.9.26.2 버전의 윈CE/LCL 인터페이스에 캘린더가 추가되었다고 한다.
그러나 이 팁은 KOL에서는 아직까지 유효하다~~ ^^

메모리 릭 체크하기

Lazarus

델파이의 빌드는 말 그대로 초스피드.
대부분의 델파이 프로그래머들은 이 엄청난 속도에 힘입어 코딩 후 즉각 실행시켜 확인하는 것이 습관처럼 되어있다. 게다가 FastMM이라는 걸출한 물건을 만난 이후부터는, 방금 만든 코드에 메모리 릭이 발생한다면 바로 바로 메시지를 띄워주기 때문에 별도의 프로파일러조차 필요없게 되었다.

그럼 라자루스는??

애석하게도 라자루스와 함께 사용할 메모리 릭 체커는 아직 찾지 못했다. 그러나 프리파스칼 컴파일러의 옵션에는 힙을 프로파일링하는 디버그옵션 "-gh"가 존재한다. 이 옵션을 잘 활용하면 FastMM만큼은 즉각적이지는 못해도 다른 도구의 도움 없이 흘린 메모리를 살펴볼 수 있게 된다.

먼저 fpc.cfg 파일을 열고 디버그 및 릴리즈 옵션을 다음과 같이 조절 한다.

#IFDEF RELEASE
  -O3
  -Xs
  -XX
#ENDIF

#IFDEF DEBUG
  -Crtoi
  -XS
  -glh
#ENDIF


-glh 는 줄단위 디버그정보와 위에서 언급한 힙 프로파일링 옵션을 켜 준다는 이야기이다. 나머지 옵션들은 fpc의 문서를 참조하자.

새 프로젝트를 생성하고 Project -> Compiler Options 메뉴에서 Other 페이지를 찾아가자.
여기에 컴파일러 지시자 -dDEBUG 를 다음과 같이 추가 해 준다.



(이렇게 개발중에는 컴파일러 지시자 -dDEBUG 를 주다가 실제 배포시에는 -dRELEASE 로 하는 것이 좋을 듯... )


프로파일된 결과물은 표준 출력으로 나오게 된다. 따라서 이 결과물을 보기 위해서는 실행파일이 콘솔을 가지고 있어야 한다. 메뉴에서 Project -> View Source 를 클릭해 *.lpr 제일 꼭대기에 다음과 같이 박아넣자.

{$ifdef DEBUG}
{$APPTYPE CONSOLE}
{$endif}


이제 프로젝트를 리빌드 한다. 한가지 문제가 있는데, 생성된 실행파일을 IDE에서 돌리는 것으로는 디버그 메시지가 표시되지 않는다는 것이다. 생성된 콘솔창이 프로그램 종료와 함께 닫혀버리기 때문이다. 따라서 커맨드 창을 띄우고 실행파일명을 직접 입력 해 주어야 한다.




프로그램을 종료하면 다음과 같이 사용된 메모리 정보가 표시된다.
뭐 아무짓도 안 했으니 메모리들이 모두 잘 반환되었다.



이제 Button1을 누를 때 마다 4바이트씩 메모리를 할당하도록 해 보자.

procedure TMainForm.Button1Click(Sender: PObj);
var
  i: PInteger;
begin
  New(i);
end;


버튼을 한 번 누르고 종료시킨 결과는 다음과 같다.




리소스릭이 발생한 유니트명과 함수명, 그리고 줄 번호까지 친절하게 표시 해 주고 있다. 만세~~!!


이 정보는 라자루스 포럼에 올라온 다음 두 개의 글타래를 종합한 것이다.

http://forum.lazarus.freepascal.org/index.php/topic,6121.0.html
http://forum.lazarus.freepascal.org/index.php/topic,5240.0.html



 

영문 WM 6.1.4 - 480*800 에뮬레이터에서 한글 사용

Lazarus/Mobile
마이미츠 동호회 강좌/팁 란을 읽다가 갑자기 눈에 확 들어온 글귀가 있었다.

주의사항:
영문 윈도 모바일 6.1에서 사용하려면 한글 로케일/폰트 및 IME 패치가 먼저 설치되어야합니다.


오호~~ 그렇다면, 에뮬레이터도 패치를 해 주면 한글 로케일이 동작한다는 이야기??
자료실을 뒤져보니 영문 WM 6.1 Pro 스마트폰 한글 입출력 및 로케일 설정 패치 라는 물건이 있다.


먼저 설치하기 전 에뮬레이터의 화면은 이렇게 생겨먹었다.




다운 받아  깔아보자. 파일명은 20090203_MyMITs_HangleNo5_+_MalgunHanja.cab 이다.




설치가 마무리 되었으면 가볍게 똥침을 넣어주자. (소프트리셋)




재부팅된 화면은 짜잔~~~ 날짜가 한글로 나온다아~~~




Notes에서 한글도 잘 입력된다.




테스트삼아 옴니아 공모전 응모게시판에 올라와있던 amdmania 님의 BGP Lunch 라는 프로그램을 돌려보았다.
그 전에는 깨져 표시되던 한글이




패치를 설치한 후에는 아주 예쁘게 보인다. ^^;;