Windows、Linux下“交叉”编译环境的搭建和测试。

来源:互联网 发布:mac u盘 10. 编辑:程序博客网 时间:2024/05/16 07:25

  开源给了我们极大的便利,使得我们能够有众多优秀的工具可以使用。

 

  作为Windows(R)下的程序开发者,经典的开发环境Microsoft Visual Studio一直是众多开发环境中及其流行的产品,——且不讨论大家使用的是否是真正经过微软授权的正版开发环境,我们无法否认VS的普及程度。

 

  而进行过Linux开发的程序员必定会对gcc的强大功能所震撼,——在Linux下的经典开发环境不是所谓的IDE,尽管IDE环境也数不胜数,但是使用commandline上的VI(VIM)/Emacs + gcc + GNUmake + gdb (+ autotools)的搭配却无可辩驳的成为Linux下产出最多的开发环境,感谢GNU项目等。

 

  开源社区正因为这些强有力的工具,成就了众多使得我们目瞪口呆的优秀作品。

 

  cygwin便是其中相当出众的一个项目。

 

  cygwin是在Windows下模拟Linux开发的及其成功的一个项目。首先我们可以想当然的认为,只要gcc等可以被移植到Windows下,那么几乎所有GNU工具就都可以移植到Windows下,——我们确实有对应的Windows版本的gcc,可是,仍然有些东西是不能直接移植到Windows下的,因为毕竟Linux不同于Windows,系统核心不同,底层细节不同,甚至没有多少相同或者类似的实现,尽管KDE3.5酷似XP,KDE4也像极了Vista。

 

  但是,cygwin使得这都变得不再重要,——将Linux的底层用Windows的底层实现,封装了众多DLL,从而将这种貌似不可能的事情变得及其简单,甚至源码包可以同在Linux下一样,./configure, make, make install,只要念三个咒,程序就装在cygwin下了。——这时候,我们得到的是“准”Windows应用程序,标准的、带有Windows经典PE文件头,可以在cmd下执行,可以通过鼠标双击运行……只是它的执行必须依赖于cygwin的一系列DLL而已。

 

  目前看来,这似乎是尽管可能不是唯一却一定是最流行的在Windows下进行Linux程序开发的途径,——只要在cygwin下可以make成功,那么,在所有兼容POSIX标准的UNIX上,不论BSDMac XSun Solaris,还是日渐流行的各种发行版的Linux,包括LinuxLiveCD,几乎都可以保证编译通过。

 

  这样,尽管我们还没有从Windows下直接生成可以在Linux下执行的可执行二进制文件,但是一般来说得到没有问题的源码已经可以了……

 

  上面讨论的是Windows下进行Linux开发,虽然有些不太靠谱,但是事实是,似乎就只是这样了;下面我们讨论我写这个东东的主要目的,——在Linux下进行Windows应用程序开发。

 

  使用虚拟机的方案已经被大家抛弃了,——如果只是这么简单,我似乎也没有写这个东东的必要了,而且,虚拟机毕竟达不到很快的,除非您的机器配置相当不错,比如流畅跑个虚拟的盗版Vista。

 

  但是不用怕,不用虚拟机照样可以做Windows应用程序开发,因为我们用的是开源的Linux,开源的GNU工具,因为我们有无所不能的巨牛的gcc。

 

  直接使用gcc的方式,不好意思,我还没试,所以就不乱说了……那么我们能用什么呢?

 

  Linux用户应该都知道有这么一个东东,号称可以将大多数Windows的二进制应用程序直接“移植到”Linux下使用,——对,这就是大名鼎鼎的wine。作为一个开发者,光知道有个wine可以用还是不够的,你要知道,我们还有个将gcc进行相关包装的winegcc

 

  winegcc的目标号称是将Windows下的程序源码尽量保持兼容的在Linux下编译通过,并生成可以在wine下执行的应用程序,——这个目标是基本可以实现的,至少我的测试是,使用Windows API的应用程序完全可以编译通过,而且生成的应用程序和Windows下的程序看上去没有什么大的差别,——当然,我的测试仅针对C程序,C++和MFC的程序没有进行测试,所以不知道是什么效果。

 

  这就很清楚了。winegcc可以编译Windows程序,但是由于使用的是winegcc相关的库,所以生成的程序尽管可以在wine中正常使用,但是将其直接搬到Windows下,将生成的.exe.so二进制文件改名为.exe后,仍然无法正常执行,——毕竟程序需要的环境已经变了。在WindowsNT的cmd下,装入程序时提示:Program too big to fit in memory,可见程序实际上已经是Windows应用程序了(虽然看上去是16位程序的文件头),只是,不算是完全的Windows应用程序。

 

  到目前这个阶段,我们已经基本达到了同Windows下使用cygwin时相似的效果,——在Linux下可以得到基本上能够移植到Windows下的程序的源码,尽管没有直接得到应用程序。这样的效果也还是可以接受的,毕竟Windows下进行“准”Linux应用程序开发时也遇到了类似的情况。

 

  当然,我们不会满足于这中源码的交叉移植,毕竟Linux环境下这么多巨牛的软件,以及Linux下"Nothing's impossible"的大众想法,我们一定可以得到直接可以运行在Windows下的程序,从Linux中编译。——因为我们有wine,以及更重要的,Linux下可以有很多使我们的工作简化的优秀工具,比如VIM(当然也有Windows版), GNUmake, sed, awk, grep...等等,不一而足。

 

  下面是我实现的环境:操作系统:Knoppix5.3 LiveCD/DVD(这是开销最小的解决方案,毕竟我没有权限给别人的机器装一个他不会用到的系统),该Live Linux自带KDE3.5,wine0.9等工具,一般的开发基本没有问题(这是一款仅提供Live服务的Linux发行版,德国制造,基本没有汉语环境,提供各种工具,号称DVD版提供多达10G以上应用程序,常见的流行程序几乎全面囊括,如gcc, gdb, GNUmake, GVIM, Emacs, Octave, LaTeX, kate, Eclipse, Kdevelop, ...其丰富程度我已经不知道应该怎么说了)。

 

  我要做的是将Windows下的经典开发环境MS VS加入Linux系统,当然使用wine。考虑到本人使用的是LiveCD,内存已划出一部分作为虚拟磁盘,所以在启动wine之后,可能已经没有太多的内存可供使用,所以没有试图将整个VS全部装到wine中,而是采用了经典迷你方案,——仅将与即将进行的测试相关的VS的组件整合一下“装”到wine中,也就是所谓的迷你VC,从命令行或者shell中进行相关工作,类似gcc的命令行开发。

 

  有关于迷你VC的相关信息,本人不再啰嗦,具体细节我会在另外的日志或其他博客比如CSDN上详述,而且鉴于一般没有使用命令行进行Windows应用程序开发的,所以建议“好事者”还是尽量将整个VCwine出来看看,如果有兴趣,当然,为了使用比如VIM,GNUmake等工具,我个人觉得,迷你VC已经足够了。——大体上的实现是这样的,将下述若干文件拷贝到相关目录,并进行一些设置就搞定。下面是我的一个例子:

 

  版本VC6.0withSP6,下述文件:$VSCOMMON/msdev98/bin下的mspdb60.dll,如果想进行资源编辑还有rc.exe,rcdll.dll等,$VSHOME/VC98/bin下的cl.exe, c1.dll, c2.dll, 如果想编译C++文件还有c1xx.dll; link.exe, 为方便项目管理还有nmake.exe,其他是不是还需要什么我记不太清了,好像就这些,拷贝到wine的C盘根目录下usr/bin目录(新建),将$VSHOME/VC98/include目录拷贝成~/.wine/drive_c/usr/include目录,$VSHOME/VC98/lib目录拷贝成~/.wine/drive_c/usr/lib目录,如果想使用MFC等,在将其他一些东西拷贝到相应目录。当然,这里目录取什么样的目录无所谓,只要后面设置时对应起来就可以。说明,前面的$VSCOMMON, $VSHOME变量没有必要存在,这只是为了说明方便而取的,代表相应的位置而已,相信搞过Windows、Linux开发的人们可以看懂。

 

  好的,迷你VC已经就位,下面是开发环境的搭建。为了方便进行命令行开发,我们需要设置wine的环境变量,从终端中输入wine regedit,打开wine的注册表编辑器,展开到HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Session Manager/Environment下,修改环境变量path,加入刚刚拷贝cl.exe等的目录在wine中的路径,比如上例是:C:/usr/bin,别忘了分号;再添加环境变量include值为C:/usr/include,lib值为C:/usr/lib,至此,迷你VC开发环境搭建成功。当然,前面的环境变量也可以直接用VI打开wine的注册表信息文件修改,——只要您觉得自己足够牛,不怕把wine搞死。不过这也算不上怎么高级,只是在文件中加几行就行。

 

  下面是Linux下进行Windows应用程序开发的例子:

 

  例一:控制台应用程序echoall,功能:显示命令行参数,显示环境变量。下面是源码:

  说明:由于文本框对跳格字符不怎么支持,所以下面的源码和makefile看上去相当混乱,敬请各位原谅!另:下述文件为了方便“观赏”,使用的是VIM对于对应文件语法高亮之后的显示样式,外加我本人对于有些标识符的“语法高亮”。

[code=C/C++]

/* Simple ANSI C file for echo the arguments */

 

#include<stdio.h>

 

int main(int argc, char * argv[], char * envp[]){

/*  */

int i;

 

printf("/t%s: Echo all arguments and environment variables./n/n", argv[0]);

 

if(argc > 1){

printf("/t%s: The commandline arguments are as followed:/n", argv[0]);

for(i = 1; i < argc; i++){

printf("%s>>>: %s/n", argv[0], argv[i]);

} /* For all commandline arguments */

}

else{

printf("/t%s: No commandline arguments specified!/n/n", argv[0]);

}

 

printf("/n/t%s: Echo all environment variables./n/n", argv[0]);

for(i = 0; envp[i]; i++){

printf("%s>>>: %s/n", argv[0], envp[i]);

} /* For all environment variables */

 

printf("/n/t%s: Task finished! Exiting.../n", argv[0]);

 

return 0;

}

[/code]

使用nmake的makefile:

# This is a simple makefile for microsoft nmake
# That is quit not a good makefile, but it just works, and I think that's enough...

 

CC = cl
RM = del

DBG = /Zi
CFLAGS = /W4 $(DBG)
OBJS = echoall.obj
SRCS = $(OBJS:.obj=.c)

DBGTMP = *.ilk *.pdb
TARGET = echoall.exe

 

$(TARGET): $(OBJS)
 $(CC) $(CFLAGS) $(OBJS) -Fe$@

$(OBJS): $(SRCS)
 $(CC) $(CFLAGS) -c $(SRCS) -Fo$@

.PHONY: clean exec

clean:
 -$(RM) $(TARGET) $(OBJS) $(DBGTMP)


exec: $(TARGET)
 $(TARGET)

!EOF(End Of File)

上述文件的使用:在shell中键入wine nmake,或者wine cmd(也可能是wcmd)后,进入wine的cmd下,直接使用nmake。但是后者因为一直在wine的cmd下,所以像VIM等工具就不能使用了。

 

使用GNUmake的makefile:

# This is a simple makefile for GNUmake
# That is not a good makefile too, but still it works...

CC = wine cl
RM = rm -f

DBG = /Zi
CFLAGS = /W4 $(DBG)
OBJS = echoall.obj
SRCS = $(OBJS:.obj=.c)

DBGTMP = *.ilk *.pdb
TARGET = echoall.exe

$(TARGET): $(OBJS)
 $(CC) $(CFLAGS) $^ -Fe$@

$(OBJS): $(SRCS)
 $(CC) $(CFLAGS) -c $< -Fo$@

.PHONY: clean exec

clean: 
-$(RM) $(TARGET) $(OBJS) $(DBGTMP)


exec: $(TARGET)
wine $(TARGET)

 

!EOF(End Of File)

上述文件的使用方式,直接在shell中键入make,可以使用GNUmake的特性,可以使用GNU的各种优秀的工具等等。

 

  例二:窗口应用程序drawlines,功能:使用鼠标在窗口中画线,可改变画笔大小和颜色。下面是源码:

[/code=C/C++]

/*
 * DrawLines, use the mouse to draw lines on the screen
 * Built buy suxpert, using cl of Visual C++, part of the Visual Studio of MS.
 *
 * Windows SDK, Win32 API ,Pure C, (Not C++ or MFC !!)
 * Suxpert at gmail dot com, 2008/11
 * */

 

#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT
0x0400

 

#include<windows.h>
#include<stdio.h>

 

#define MAGIC_COLOR 0x010305

 

LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM );

 

int APIENTRY WinMain( HINSTANCE hInstance,
  HINSTANCE hPrevInstance,
  LPSTR lpszCmdLine,
  int nCmdShow )
{

 /* The Entry for windows program, just like main() in dos */

 WNDCLASS wc;
 HWND hwnd;
 MSG msg;

 wc.style = CS_HREDRAW | CS_VREDRAW;  // Class style
 wc.lpfnWndProc = (WNDPROC)WndProc;  // Window procedure address
 wc.cbClsExtra = 0;    // Class extra bytes
 wc.cbWndExtra = 0;    // Window extra bytes
 wc.hInstance = hInstance;   // Instance handle
 wc.hIcon = LoadIcon( NULL, IDI_WINLOGO ); // Icon handle
 wc.hCursor = LoadCursor( NULL, IDC_ARROW ); // Cursor handle
 wc.hbrBackground = (HBRUSH)( COLOR_WINDOW + 1 );// Background color
 wc.lpszMenuName = NULL;    // Menu name
 wc.lpszClassName = "DrawLines";   // WNDCLASS name

 RegisterClass( &wc );

 hwnd = CreateWindow(
   "DrawLines",   // WNDCLASS name
   "DrawLines",   // Window title
   WS_OVERLAPPEDWINDOW,  // Window style
   CW_USEDEFAULT,   // Horizontal position
   CW_USEDEFAULT,   // Vertical position
   CW_USEDEFAULT,   // Initial width
   CW_USEDEFAULT,   // Initial height
   HWND_DESKTOP,   // Handle of parent window
   NULL,    // Menu handle
   hInstance,   // Application's instance handle
   NULL    // Window-creation data
   );

 ShowWindow( hwnd, nCmdShow );
 UpdateWindow( hwnd );

 while( GetMessage(&msg, NULL, 0, 0) ){
  TranslateMessage(&msg);
  DispatchMessage(&msg);
 }

 return msg.wParam;
} // WinMain


LRESULT CALLBACK WndProc( HWND hwnd,
  UINT message,
  WPARAM wParam,
  LPARAM lParam )
{
    /* Windows will call this function anytime... */

 PAINTSTRUCT ps;
 HDC hdc;
 RECT rt;
 HFONT textFont, OldFont;
 int centx, centy;
 int x, y;
 static int xx, yy;
 static COLORREF pen_color = 0x000000;
 static int pen_size = 3;
 char info[64];

 HPEN pen, oldpen;

 

 textFont=CreateFont(16,10,0,0,0,0,0,0,0,0,0,0,0,"Courier New");

 

 switch(message){

 case WM_KEYDOWN:

  switch(wParam){
   case VK_ESCAPE:
    PostQuitMessage(0);
    break;
  }// switch wParam

  break; // case WM_KEYDOWN
 case WM_LBUTTONDOWN:

  /* Store the position of the mouse */
  xx = LOWORD(lParam);
  yy = HIWORD(lParam);

  /* Hidden the cursor */
  ShowCursor(FALSE);

  break; // case WM_LBUTTONDOWN

 case WM_LBUTTONUP:

  /* Show the cursor */
  ShowCursor(TRUE);

  break; // case WM_LBUTTONUP

 case WM_MOUSEWHEEL:

  /* Change the pen_color */
  hdc = GetDC(hwnd);

  OldFont = SelectObject(hdc, textFont);
  SetTextColor(hdc, pen_color);

  switch( LOWORD(wParam) ){

   case MK_RBUTTON:

    if( (int)wParam > 0 && pen_size < 64){
     pen_size++;
    }
    else if(pen_size > 1){
     pen_size--;
    }

    sprintf(info, "Current Pen Size: %8d  ", pen_size);
    TextOut( hdc, 0, 0, info, strlen(info) );

   break;

   default: //case MK_LBUTTON:

    if( (int)wParam > 0 ){
           if( pen_color < 0xFFFFFF - MAGIC_COLOR)
      pen_color += MAGIC_COLOR;
           else
      pen_color = 0;
    }
    else{
           if(pen_color >= MAGIC_COLOR)
      pen_color -= MAGIC_COLOR;
           else
      pen_color = 0xFFFFFF;
    }

    sprintf(info, "Current Pen Color: %0#8X ", pen_color);
    TextOut( hdc, 0, 0, info, strlen(info) );

   break;

  }

  SelectObject(hdc, OldFont);
  ReleaseDC(hwnd, hdc);

  break; // case WM_MOUSEWHEEL

 case WM_MOUSEMOVE:
  hdc = GetDC(hwnd);

  OldFont = SelectObject(hdc, textFont);
  SetTextColor(hdc, pen_color);

  /* Get the position of the mouse */
  x = LOWORD(lParam);
  y = HIWORD(lParam);

  switch(wParam){

  case MK_LBUTTON:

   sprintf(info, "Drawing. Mouse: %5d, %5d ", x, y);
   TextOut( hdc, 0, 0, info, strlen(info) );

   pen = CreatePen(PS_SOLID, pen_size, pen_color);
   oldpen = SelectObject(hdc, pen);

   MoveToEx(hdc, xx, yy, NULL);
   LineTo(hdc, x, y);
   xx = x;
   yy = y;

   SelectObject(hdc, oldpen);
   DeleteObject(pen);

   break; // left button was down

  }

  SelectObject(hdc, OldFont);
  ReleaseDC(hwnd, hdc);
  break; // case WM_MOUSEMOVE


 case WM_PAINT:

  hdc = BeginPaint( hwnd, &ps );
  GetClientRect(hwnd, &rt);
  centx = (rt.left + rt.right)/2;
  centy = (rt.top + rt.bottom)/2;

  pen = CreatePen(PS_SOLID, pen_size, pen_color);
  oldpen = SelectObject(hdc, pen);

  Ellipse( hdc, rt.left, rt.top, rt.right, rt.bottom );
  // Here we Draw an ellipse in the window of our program

  SelectObject(hdc, oldpen);
  DeleteObject(pen);
  EndPaint( hwnd, &ps );

  break; // case WM_PAINT

 case WM_DESTROY:

  PostQuitMessage(0);
  break; // case WM_DESTROY

 default:
  return DefWindowProc( hwnd, message, wParam, lParam );

 }

 DeleteObject(textFont);

 return 0;
}

[/code]

使用nmake的makefile:

# Makefile for DrawLines: using NMAKE & cl
#

CC = cl
RM = del
OBJS = DrawLines.obj
LIBS = kernel32.lib user32.lib gdi32.lib
CFLAGS = /W4 /GA /Zi

SRCS = DrawLines.c

TARGET = DrawLines.exe

DBGINF = *.pdb *.ilk

$(TARGET): $(OBJS)
 $(CC) $(CFLAGS) $(OBJS) $(LIBS) /Fe$@

$(OBJS): $(SRCS)
 $(CC) $(CFLAGS) -c $(SRCS) /Fo$@


.PHONY: clean exec cleanall eraseback

clean:
 -$(RM) $(TARGET) $(OBJS) $(DBGINF)


eraseback:
 -$(RM) *~

cleanall: clean eraseback
 @echo Clean all back files(*~) and build out files!
 @echo Only source files are left!

exec: $(TARGET)
 $(TARGET)

 

!EOF(End Of File)

上述文件的使用同前例相同。

 

使用GNUmake的makefile:

# Makefile for DrawLines: using GNUmake, wine & cl
#

CC = wine cl
RM = rm -f
OBJS = DrawLines.obj
LIBS = kernel32.lib user32.lib gdi32.lib
CFLAGS = /W4 /GA /Zi

SRCS = DrawLines.c

TARGET = DrawLines.exe

DBGINF = *.pdb *.ilk

$(TARGET): $(OBJS)
 $(CC) $(CFLAGS) $^ $(LIBS) /Fe$@

$(OBJS): $(SRCS)
 $(CC) $(CFLAGS) -c $< /Fo$@


.PHONY: clean exec cleanall eraseback

clean:
 -$(RM) $(TARGET) $(OBJS) $(DBGINF)


eraseback:
 -$(RM) *~

cleanall: clean eraseback
 @echo Clean all back files(*~) and build out files!
 @echo Only source files are left!

exec: $(TARGET)
 wine $(TARGET)

 

!EOF(End Of File)

由于使用的是GNUmake,同上所述,可以使用GNUmake的特性。

 

  经测试,上述工程在knoppix&wine环境下进行开发,可以得到直接在Windows下可以执行的二进制文件.exe,测试成功!