2011年9月18日 星期日

在Makefile裡面的command line設定變數值

在Makefile裡面的格式如下所示

target : prerequisites
command


請參考nmake 簡單教學

一般來說,要設定變數,只能在Makefile的最上面的部分
在上面那一個文章的Makefile的範例裡面,就是在everything之上設定變數

但是,我現在想做的是,依不同的target設定不同的數值~也就是想把設定數值的部分擺在command裡面。但是,在nmake的環境下,我在設定完後,直接印出來,似乎沒有達到效果。

target : prerequisites
SET TEST_VALUE=YES
ECHO $(TEST_VALUE)


最後,在多方嘗試之後,發現是可以達到我的需求的,但是,有以下幾點限制
1. 在目前的Makefile的command line設定的變數,必需觸發的下一個Makefile內才能使用
2. 變數必需不能有小寫

直接看例子吧~

Hello.c
#include <stdio.h>

int
main (
)
{
printf ("Good Morning!\n");
return 0;
}


例子一:
First.makefile
all:
[tab] SET TEST_VALUE=YES
[tab] nmake /f Second.makefile all

Second.makefile
# Program, flags, etc.
ASM = cl
OBJ = Hello.obj
TARGET = Hello.exe

all: clean everything
!IFDEF TEST_VALUE
[tab]ECHO $(TEST_VALUE)
!ENDIF

everything: $(TARGET)

clean:
[tab]- del $(OBJ) $(TARGET)

.c.obj:
[tab]$(ASM) $< /Fo$@

$(TARGET): $(OBJ)
[tab]$(ASM) $(OBJ) /Fe$@


執行指令
%> nmake /f First.makefile

如此一來,就可以在command line設定變數了,並且可以使用了。

但是,這樣的程式碼實在很難看~
那就用遞迴的方式,自己的Makefile呼叫自己的Makefile
Makefile
# Program, flags, etc.
ASM = cl
OBJ = Hello.obj
TARGET = Hello.exe

!IFDEF TEST_VALUE
all: clean everything
[tab]ECHO $(TEST_VALUE)
!ELSE
all:
[tab]SET TEST_VALUE=YES
[tab]$(MAKE) $@
!ENDIF

everything: $(TARGET)

clean:
[tab]- del $(OBJ) $(TARGET)

.c.obj:
[tab]$(ASM) $< /Fo$@

$(TARGET): $(OBJ)
[tab]$(ASM) $(OBJ) /Fe$@


執行指令
%> nmake

這一個Makefile會被執行兩次,在第一次的時候,TEST_VALUE變數並沒有被定義,
而第二次的時候,TEST_VALUE有被定義,如此一來,只需要一個Makefile就可以了

[2011.11.02 補充]
在make裡面,可以在target裡另外指定變數的值
foo = abc

all: foo = xyz
all:
[tab]echo $(foo)

這個時候的foo的值為xyz
以下的語法也提供相同的功能
all: override foo = xyz
all: export foo =xyz

但是,這個方法,似乎只能在linux下,正統的make下使用~
nmake並不支援這個語法~

參考資料:
Makefile 語法簡介

2011年9月5日 星期一

把exe與需要用到的DLL包成一個執行檔

最近為了使用Qt寫一些小工具,在研究如何把需要用到的library(DLL)在build time的時候就包進執行檔中~
把過程寫到如何build出不需要另外建置環境的QT exe檔
果然最後,還是沒有成功~~
目前release暫時的方法就是把需要用到的DLL檔跟執行檔放在一起@@,這樣就算使用者沒有Qt的環境,也是可以執行成功的。

現在找到另一個替代方案,就是如何把已經build出來的exe與其會用到的DLL甚至其它檔案包在一個exe檔案中,並且當使用者直接點兩下,可以執行我們的應用程式~~

目前找到winrar與7z(需另外安裝7z-sfx),與另一個好用的小tool 7-Zip SFX Maker(底層應該也是用7z-sfx完成的)
另外EXE Bundle - The EXE Binder,沒有試過

winrar:
1. 嘗試把要包在一起的檔案壓縮,並設定「建立自我解壓縮檔」

2. 到「進階設定」→「自解檔選項」
一般設定→解壓縮路徑→建立於現用的資料夾
一般設定→安裝程式→解壓縮之後執行→Calc.exe
模式→暫存模式→解幫裝至暫存資料夾
模式→安靜模式→全部隱藏

就會產生一個Winrar.exe的檔案,然後,直接執行它就可以了!!

7z-sfx (手動):
1. 先到7z網站下載SFX模組

2. 在製作自動解壓縮檔,只需要用到7zr.exe 與 7zSD.sfx這兩個檔案。
如何使用sfx,在Install資料夾裡面有範例~
3. 先把要包的檔案壓成7z

4. 編譯config.txt
這是設定 SFX for installers 在執行的時候的行為,包括解壓縮時要顯示什麼標題(Title)、是否要提示使用者按下確認後進行解壓縮(BeginPrompt)、或解壓縮後要自動執行哪支程式(RunProgram)、...等等。
;!@Install@!UTF-8!
Title="Simple Calc"
BeginPrompt="Do you want to run calc?"
RunProgram="Calc.exe"
;!@InstallEnd@!

詳細使用sfx的方式可以參考7z commmand的help裡面有說明~~



5. 把7zSD.sfx、config.txt、以及 Release.7z合併成執行檔,必需要透過windows內建的copy /b來完成~
copy /b 7zS.sfx+config.txt+Release.7z Release.exe
就會產生一個Release.exe的檔案,然後,直接執行它就可以了!!

7zip 進階自解壓縮檔產生工具 (自動):

7-ZIP SFX Maker (自動):

[2011.10.09 補充]
制作安裝檔
軟體王 - Inno Setup
Inno Setup

參考資料:
[問題]請問能將EXE和DLL包在一起成為一個可執行檔嗎?
如何利用 7-zip 設定自動解壓縮並解壓縮後自動啟動程式
7z-sfx 把7z檔案 製作成自解壓縮
製作屬於自己的WinRAR自解檔
7zSfxTool3.6.1.200(7z自解壓縮設定工具) 繁體中文

2011年9月2日 星期五

用Qt Creator寫一個簡單的計算機

之前有寫過一篇文章[Qtcreator - Qt的IDE],記錄Qt Creator的初體驗~
不過,當時也只是build完Qt的demo example後,覺得用Qt可以寫成這一些程式,覺得好厲害~
當時仍然以linux的環境為主,所以,初次嘗試就到此為止。

現在的主要環境改到Windows了(但是,我還是沒有放棄linux,有時候還是會玩一下)
要寫一些視窗介面的tool,因為,我用VC IDE寫程式的經驗幾乎是0,所以,我打算還是用Qt在windows下寫程式了。

2011年8月29日 星期一

alignment in the C

在建立struct時,有時候裡面的成員的排序很重要。
排序不同,會造成sizeof整個struct的值不同。

例如以下的例子:
struct aaaaa
{
  char a;
  int b;
  char c;
};

-byte數為12-

struct aaaaa
{
  char a;
  char c;
  int b;
};

-byte數為8-

========== START ==========

struct 的 alignment 有兩個, 首先是個別成員的 alignment, 然後是整個 struct 的 alignment.

首先, 每一個類型都會有它自己的 alignment requirement. 每個物件在記憶體裏的相對位置是:
  Offset % alignment-requirement == 0

假定在你的編譯器上, char 的 alignment requirement 是 1, int 的 alignment requirement 是 4.

在第一個 struct 裏, a 是在 offset 0 的位置, 而 b 則須在 offset 4 的位置. a 跟 b 之間會有 3 個 byte 的空格.

個別成員的 alignment 完成後就要做整個 struct 的 alignment. 這個關係到最後一個成員後面要加多少個 padding. 需要這個的原因是當你以 struct 來定義一個陣列的時候, 每一個 struct 元素都需要 align. 這時就要以到 struct 裡面最大的成員來做決定.

所以第一個 struct 最後一個成員 c 後面需要 3 個 bytes 的 padding. 這樣在陣列的架構下, 下一個元素的 b 也會 align:

    +=======+
  0 |a|x|x|x|
    |-------|
  4 |b      | 
    |-------|
  8 |c|x|x|x|
    +=======+
 12 |a|x|x|x|
    |-------|
 16 |b      |
    |-------|
 20 |c|x|x|x|
    +=======+
    |       |

第二個 struct 的 padding 結構:

    +=======+
  0 |a|c|x|x|
    |-------|
  4 |b      |
    +=======+
  8 |a|c|x|x|
    |-------|
 12 |b      |
    +=======+

而要解決此問題,最好在每一個struct裡面都放相同alignment的變數,
或是使用#pragma pack ()
A1:
#pragma pack(push, 1)
struct aaaaa
{
  char a;
  char c;
  int b;
};
#pragma pack(pop)

其中 "push" 的意思是先將原來的設定(可能是 4)暫存起來;而 "pop" 則是恢復原來的設定。

A2:
#pragma pack(1)
struct aaaaa
{
  char a;
  char c;
  int b;
};
#pragma pack(1)

意思是說,強制把alignment設定為1 byte

參考資料:
Visual C++ 的 Struct Memeber Alignment 問題與解決方法
C/C++ struct 的 alignment

Bit-field of struct

Bit-field 是一種省空間的特殊 data member, 可以使用特定幾個 bit 來放 data.

#include <stdio.h>

struct Data_Detail
{
  unsigned short s1 : 1;
  unsigned short s2 : 3;
  unsigned short s3 : 4;
  unsigned short s4 : 8; // 剛好塞滿一個 16 bit 之 unsigned short
};

typedef struct _Data_View {
  char v1 : 1;
  char v2 : 1;
  char Reserve : 6; // 保留6 bit,當以後需要可以使用
} Data_View;

int
main (
  )
{

{
  Data_View X;
  X.v1 = 0;
  X.v2 = 1;
  printf ("X.v1 is %d\n", X.v1);
  printf ("X.v2 is %d\n", X.v2);
}

{
  struct Data_Detail Value;
  Value.s1 = 1;
  Value.s2 = 2;
  Value.s3 = 3;
  Value.s4 = 4;
  printf ("Value.s1 = %d\n", Value.s1);
  printf ("Value.s2 = %d\n", Value.s2);
  printf ("Value.s3 = %d\n", Value.s3);
  printf ("Value.s4 = %d\n", Value.s4);
}

  return 0;
}


建議同一個struct裡面的Bit-field是同一型別,避免因為不同型別而導致錯亂。

若不同的type放在同一個struct裡面,我猜想每一個同型別所需要的空間會算在一起。
例如:
char v1 : 1與char v2 : 1與char Reserve : 6所需要的空間一共是8bit,剛好就是一個char。
所以,用sizeof (Data_View)會是1 (byte)


若在Data_View的結構裡面,加入一個unsigned short s4 : 1
其中unsigned short 是 2 bytes
用sizeof (Data_View)的值會是4,我猜原因是char與unsigned short是分開算的,並且以其中一個最大的為單位,
因為最大是unsigned short是2 bytes,所以,一共是4bytes。

不過,我想這個結果可能跟不同的Compiler不同,而結果有所不同。

參考資料:
Union and Bit-field

2011年8月25日 星期四

在win7下建立一個external partion

在WIN7下,並沒有辦法直接透過Disk Management去分割出一個Extended Boot Record(延伸磁區)

但是,仍然可以透過WIN7內建的指令來建立Extended Boot Record,

1. 透過administrator去開啟cmd
2. > diskpart
3. DISKPART> list disk
4. DISKPART> select disk 0
5. DISKPART> create partition extend size = 40960
6. DISKPART> create partition logical size = 40960

把id改為84
7. DISKPART> list disk
8. DISKPART> select volume = x
9. DISKPART> set id=84 override


其它:
delete partition [noerr] [override]
delete partition 指令可以刪除目前設為焦點的磁碟分割。
Diskpart 不允許刪除目前的系統、開機或分頁磁碟區。若要刪除 ESP、MSR 或已知的 OEM 磁碟分割,您必須指定 override 參數。

參考資料:
[Tutorial] 如何在 Windows Vista / 7 下分割延伸磁區 (Extended Partition) ?
Diskpart 指令行公用程式的說明

2011年8月23日 星期二

讓程式碼的function內的component比較容易被抽換

讓程式碼的function內的component比較容易被抽換


當一個函式很大時,裡面包含許多component,為了程式碼容易看,
在程式中可以透過大括號把一個函式分成許多部分~

如以下的例子:

#include <stdio.h>

int
main (
)
{
  int y = 2;

  printf ("Hello, Y value is %d\n", y);

#ifdef FOMLOAD
{
  int x = 1;
  printf ("X value is %d\n", x);
  printf ("Y value is %d\n", y);
}
#endif //#ifndef FOMLOAD

//  printf ("X value is %d\n", x); // variable x does not exist in this scope

  return 0;
}


如此一來,被FOMLOAD包起來的程式碼,就可以依照FOMLOAD在Build Time的時候就決定要不要被執行~
這樣一來程式碼比較容易讀,在抽取的Component時,也比較容易。

另外,在上面的例子中,在大括號被宣告的int x變數,其能被存取的也只有在大括號中。

如此一來,更可以節省一些記憶體的空間。

另外,為了讓程式設計師比對有使用這一個方式跟沒有的差別。
以下是沒有使用這一個大括號的設計方式的程式碼;

#include <stdio.h>

int
main (
)
{
#ifndef FOMLOAD
  int x = 1;
#endif //#ifndef FOMLOAD
  int y = 2;

  printf ("Hello, Y value is %d\n", y);

#ifdef FOMLOAD
  printf ("X value is %d\n", x);
  printf ("Y value is %d\n", y);
#endif //#ifndef FOMLOAD

//  printf ("X value is %d\n", x); // variable x does not exist in this scope

  return 0;
}


如此一來,被FOMLOAD被分散在兩個地方,目前這一個例子的程式碼比較小,可能看不出差異,
當程式碼很大時,這兩個部分可能會被分散在好幾行之後(因為,宣告變數一定要在function的開頭)
這樣程式在可讀性上,就比較差。

Makefile
# Program, flags, etc.
CC = cl
CCFLAGS = /c
OBJ = WithCurelyBrace.obj
TARGET = WithCurelyBrace.exe

!IFDEF NOTSHOW
FLAGS =
!ELSE
FLAGS = /DFOMLOAD
!ENDIF

everything: $(TARGET)

clean:
[tab]del $(OBJ) $(TARGET)

all: clean everything

.c.obj:
[tab]$(CC) $(CCFLAGS) $< /Fo$@ $(FLAGS)

$(TARGET): $(OBJ)
[tab]$(CC) $(OBJ) /Fe$@


指令如下:
nmake all NOTSHOW=1
不會顯示括號內的指令

nmake all
會顯示括號內的指令