將Linux代碼移植到Windows的簡單方法

將Linux代碼移植到Windows的簡單方法

 
 

  一.前言html

  Linux擁有豐富各類源代碼資源,可是大部分代碼在Windows平臺狀況是沒法正常編譯的。Windows平臺根本沒法直接利用這些源代碼資源。若是想要使用完整的代碼,就要作移植工做。由於C/C++ Library的不一樣和其餘的一些緣由,移植C/C++代碼是一項困難的工做。本文將以一個實際的例子(Tar)來講明如何把Linux代碼移植到Windows平臺上。移植過程將盡可能少修改代碼,以便代碼的運行邏輯不會發生任何變更。保留絕大部分軟件主要功能。linux

  二.準備工做編程

  Tar是Linux平臺下面一個打包工具。移植這樣一個程序到windows平臺須要作那些工做呢?windows

  首先是一些準備工做,在Windows平臺上面安裝上Cygwin的最新版本,在Cygwin中安裝好GCC等開發工具。 一樣也須要一個Windows開發環境。可使用最新版本Visual Studio, Microsoft Visual Studio .NET 2003。從www.gnu.org上取得Tar的最新源代碼,版本是1.13。在Cygwin下面解開tar-1.13.tar.gz.源代碼包。注意請不要在Windows下面使用WINRAR或者WINZIP來解壓縮。 WINRAR和WINZIP在解壓縮某些tar.gz包的時候會有問題。使得解包以後的目錄和文件出現異常。若是是源代碼包將有可能不能在Cygwin下面正確編譯。解開壓縮包以後,進入 tar-1.13目錄,在當前的目錄下面輸入安全

./configure多線程

  命令,運行完畢以後,再次輸入app

makeide

  命令。開始編譯tar的Cygwin版本。函數

  編譯基本上不會有問題,進入src目錄,能夠看到新編譯好的Tar程序tar.exe。工具

  Cygwin是一個API層的Linux模擬環境。若是可以在Cygwin下面編譯,運行。實際上也就是能在Windows下面編譯和運行,只是須要有一層中間API模擬某些Linux特有的操做。簡單的判斷一個Linux程序能不能移植到Windows平臺下面,就是看是否能在Cygwin下面編譯源代碼,並運行程序。

  在Cygwin中編譯Tar的源代碼,判斷可否移植只是其中一個緣由。另一個緣由是移植代碼過程當中須要一個特殊的頭文件config.h。config.h是移植過程當中最重要的源代碼文件。Config.h文件並非源代碼自己的一部分。文件是在Cygwin下面運行」./configure」命令時生成的。在Cygwin下運行」./Configure」命令時,會根據Cygwin平臺開發環境生成config.h文件。編譯時也須要config.h文件對代碼編譯項進行控制。移植工做也以config.h文件爲基礎。

  接下來就是構造Windows工程。先用Visual Studio .NET 2003建立一個空的工程(Project),命名爲WinTar。根據Cygwin中的編譯輸出信息,Tar主要的代碼在Src和lib兩個目錄中。把這兩個目錄複製到新工程裏,並把代碼加入到工程中。而後複製Config.h到WinTar工程目錄下面。

  準備工做基本上完成了,接着就是移植。移植過程能夠分爲3個部分。

  三.第一個目標:使得WinTar能編譯過(Compiler)

  第一個目標的完成主要圍繞Config.h來實現。Linux下開發環境和Windows開發環境很大的不一樣是C Library頭文件和各類類型的定義不一樣。而Config.h提供了完整編譯開關來處理由於不一樣平臺間開發環境不一樣帶來的不一樣之處。如今須要手工去修改這個文件,以便Tar源代碼能適應Windows平臺。

  首先調整各類C Library頭文件(Header File)的包含問題。在Config.h中定義了不少相似HAVE_XXXX_H。好比定義HAVE_CONFIG_H爲1表示工程中可使用config.h。

  #define HAVE_MALLOC_H 1表示能夠在工程中使用Malloc.h頭文件。經過調整這些定義值,能夠去除一些Windows平臺下面沒有的頭文件包含。也許其餘地方還有不少頭文件包含關係須要處理,可是這裏的定義基本上解決了大部分的頭文件包含問題。

/* Define if you have the <linux/fd.h> header file. */
/* #undef HAVE_LINUX_FD_H */

/* Define if you have the <locale.h> header file. */
#define HAVE_LOCALE_H 1

/* Define if you have the <malloc.h> header file. */
#define HAVE_MALLOC_H 1

/* Define if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1

/* Define if you have the <ndir.h> header file. */
/* #undef HAVE_NDIR_H */

  第二步,調整各類數據類型的定義,可能在linux下面會有不少特殊的數據類型定義,Config.h文件中也包含了一部分能夠變更的數據類型定義項。這些定義通常都是基本數據類型的重定義。能夠根據Windows平臺下的數據類型定義狀況進行修補。好比在Cygwin的開發環境中有個數據類型mode_t, Visual Studio的C Library中卻(做者 很土,聯繫方法 jackforce at 163 dot com)找不到這樣數據類型。Tar代碼中使用了大量的mode_t數據類型. config.h中提供了修改項來讓開發人員本身修改mode_t的定義,並提示若是mode_t在<sys/types.h>中沒有定義的話,能夠把他定義爲int型。因此在config.h加上#define mode_t int。這樣mode_t沒有定義的問題就解決了。其餘的數據類型也是一樣對待處理。

* Define to `int' if <sys/types.h> doesn't define. */
#define mode_t int

/* Define to `long' if <sys/types.h> doesn't define. */
/* #undef off_t */

/* Define to `int' if <sys/types.h> doesn't define. */
#define pid_t int

  第三步,調整各類函數定義。在Config.h中除了HAVE_XXXXX_H以外還有一種預約義,HAVE_XXXX。 這是一些可選用函數定義開關。#define HAVE_MEMSET 1 表示工程中可使用memset函數。也就是說工程用到的類庫中已經實現了這個函數。若是沒有,那麼就須要#undef HAVE_MEMSET,固然也能夠本身提供這些函數。

/* Define if you have the memset function. */
#define HAVE_MEMSET 1

/* Define if you have the mkdir function. */
#define HAVE_MKDIR 1

/* Define if you have the mkfifo function. */
#define HAVE_MKFIFO 1

/* Define if you have the munmap function. */
#define HAVE_MUNMAP 1

  最後,Config.h文件中除了上面的頭文件,函數,數據類型編譯選項以外,還有其餘一些東西,好比環境變量,其餘編譯選項。這些內容會根據不一樣的項目而有很大的不一樣。可是能夠從Config.h基本看出移植的工做量有多大。

  通過上面的調整以後,勢必(做者很土,其餘文章 請查看vchelp很土專欄)由於Windows環境下沒有某些頭文件,好比poll.h,就會沒有poll函數,沒有dirent.h 就會沒有dirent 結構體。而繼續使得WinTar編譯不過。這個時候就須要根據具體的編譯錯誤信息進行細節修飾。當須要使用Windows下一些特殊的定義的時候請不要忘了在Config.h的最前面加入#include <Windows.h>. 
關於細節修飾,舉個例子來講明。好比有個選項HAVE_INTTYPES_H

/* Define if <inttypes.h> exists, doesn't clash with <sys/types.h>,
and declares uintmax_t. */
#define HAVE_INTTYPES_H 1

  經過分析代碼能夠發現,代碼並非須要一個完整的inttypes.h文件,而是爲了一個uintmax_t的定義。在Visual Stdio的C Library中並無inttypes.h這個文件,也沒有uintmax_t這個定義。回溯Cygwin的include目錄的inttypes.h文件,發現了uintmax_t的定義

typedef unsigned long long uintmax_t;

  很簡單的數據類型重定義。這麼簡單定義,徹底能夠從Cygwin的Include目錄中單獨拿出來作一個專用版本的inttypes.h加入到WinTar項目中。這樣編譯過程當中uintmax_t沒有定義的問題就解決了。解決這類問題的通常的作法也就是從Cygwin的Include目錄裏面拿出相關的頭文件進行修改或者單獨複製到WinTar的目錄下面。[本文於2003年完成. 如須要轉載 請聯繫jackforce at 163 dot com ]修改或者複製代碼的原則是再也不引入更多的定義或者頭文件,僅取所需部分。其餘相似的問題還有direct結構定義和相關函數。

  在編譯過程當中,不少錯誤是有由lib目錄下的文件產生的,可是lib目錄下的文件不是徹底都須要的。lib目錄只是一個對Tar的補充庫。須要的代碼才須要編譯。 具體判斷的方法一個是參考Windows C Library庫的內容。若是一樣的函數,數據類型已經定義,就不須要Lib目錄中的相同數據類型的定義和函數實現了。還有一個方法是儘可能去掉lib目錄中的C文件,只保留頭文件,並使得編譯可以經過,根據link的錯誤信息去檢查那些lib中的C文件是須要的。

  除了修改外圍的各類頭文件以外,還不要忘了修改工程的編譯選項,特別是預約義選項。在Tar的移植過程就須要如下的預約義HAVE_CONFIG_H,_POSIX_SOURCE,MSDOS。HAVE_CONFIG_H 表示程序編譯須要config.h文件。爲了方便期間,在tar移植過程當中就放到工程的預編譯選項中了。MSDOS,移植的是Linux下的控制檯程序,而Windows平臺最接近Linux控制檯就是DOS,特別是一些環境變量設置和全局常量的定義。Tar的有些代碼針對MSDOS環境已經作了一部分修正,這點在移植過程當中能夠利用起來。還有一個可選項是__CYGWIN__。有些Linux程序會針對Cygwin平臺作出代碼上的特殊設定。當遇到這樣的代碼的時候,必定要加上__CYGWIN__預約義項,可以大大減小移植須要的工做量。還有就是移植過程引入的各類Cygwin代碼中也可能須要__CYGWIN__定義(有時候是其餘的定義,好比_POSIX_SOURCE,或者__INSIDE_CYGWIN__)。

  通過上述的幾個步驟。第一個目標,代碼可以編譯經過基本上是不會有什麼問題的。只要把握好二個修改代碼的基本原則,第一。引入新的代碼,而不修改原有的代碼。在沒有辦法進行調試前修改源代碼是不容許的,修改的很差就會引發最後代碼運行邏輯的混亂,並且在代碼可以運行以前是很難發現問題的。因此除非很是有把握,不然不要修改被移植工程的源代碼。第二,引入新的代碼以後,不能由於此次引入而須要再次引入新的代碼。這樣子,就進入死循環了。爲了解決某個數據類型的定義,而引入了新的不能解釋的數據類型。這樣還不如不引入新的代碼。因此引入新的代碼,特別是不少頭文件。引入以前必定要作修改,只保留工程自己須要的部分,去除那些不須要的代碼。直到能編譯經過爲止。 三:第二個目標,使得代碼可以連接過(Link)

  完成了第一個目標以後,就會有大量的link錯誤。緣由是前面引入了不少外部函數,外部全局常量只有定義而沒有實體,因而就會產生link錯誤。如今須要的是爲代碼提供引入的函數實體,外部全局變量實體。通常都是函數link(本文於2003年完成. 如須要轉載 請聯繫jackforce at 163.com)不到的比較多。

  要解決link錯誤就須要瞭解不一樣平臺上面函數操做的區別,特別是某些概念的區別。這裏最好的參考資料有兩個。一個是Windows Services for UNIX (SFU)的幫助文件,一個是MSDN中的一篇文章《UNIX Application Migration Guide》。SFU是微軟提供一個Unix兼容環境,有點像Cygwin。在安裝上SFU以後有一個幫助文件。其中有一部分就是Unix,Linux函數的說明,有些函數提供了信息說明能夠用Windows Library中那些函數來替代。這點對於移植是很重要的(省事)。UNIX Application Migration Guide應該不算文章而是有點像書了。它說明了不少windows和Unix系統(類Unix系統)中不少概念不一樣之處,針對這些不一樣的概念提供了不少相關的信息來講明如何進行模擬這些不一樣之處。好比Unix系統中Signals概念可使用Windows環境中的Event來替代。SIGALRM用Windows Message來替代等。

  SFU的幫助文件提供了一部分信息來講明Windows平臺中哪些低階函數(C 函數庫)能夠替代相關Unix函數。《UNIX Application Migration Guide》則提供了一種方法來轉換Unix平臺上的一些OS級的概念到windows上。實際上Cygwin下面也作了不少這樣的轉換。具體解決link問題的時候能夠參考Cygwin自己的實現。

  不過有些概念,好比安全權限方面的概念。在Linux平臺和windows平臺上面是徹底不能互換的。並且windows平臺中的權限函數操做(本文於2003年完成. 如須要轉載 請聯繫jackforce@163.com)的過於複雜。這樣對於某些linux函數。好比getuid處理能夠參考Cygwin的處理辦法。什麼也不作直接返回0 (return 0)。當代碼中遇到這些函數的時候能夠從Cygwin的代碼中複製一個getuid出來。放入工程中去。
利用這些資料,並經過相關的工具好比sourceinsight來搜索Cygwin自己的源代碼,Link問題並不難處理。只是有可能在處理link問題的過程當中會回覆到上面的問題,編譯不過。這個時候的代碼修改仍是必定要注意不要引入太多的新的代碼,省得問題愈來愈複雜。

  四:代碼運行正常

  實際上當link問題解決以後,程序能夠在windows環境中運行時,一切就盡在掌握了。若是不考慮作多平臺的程序的話,這個時候就能夠任意去修改程序了。不過在代碼調試過程可能須要一個參照,看看正常的程序運行流程是怎麼樣的。剛剛移植過來的程序在不少地方並不能立刻就能正常的運行。回到Cygwin中,從新編譯一個能夠調試的版本(在GCC編譯選項加上-g3),在須要的時候能夠在Cygwin中調試程序。調試能夠用GDB或者Insight。若是習慣Windows 平臺下面編程,可使用Insight,這是一個TCL/TK腳本程序,它提供了一個Windows界面以方便用戶調試程序,不過Insight最終仍是調用GDB。在這裏具體調試就不細說明了。

  五:多平臺代碼

  移植後的代碼(本文於2003年完成. 如須要轉載 請聯繫jackforce@163.com)若是須要在多個平臺上面運行,就要在lib目錄裏面大作文章了。提供本身的函數庫,並根據各個平臺進行調整。Tar的代碼由Config.h和一些編譯選項來控制如何在各個不一樣的平臺上面作編譯。Lib則提供了不少C Library函數或者不一樣平臺下面的其餘函數的替代版本。這樣Tar在編譯過程當中就不會由於某些平臺下某些函數的缺失而編譯不過。多平臺支持,通常都是在代碼中加上不少編譯開關,在編譯期間去分隔Linux,Windows或者其餘平臺下面的特殊代碼。好比utime.h頭文件的包含問題。由於文件在Linux(gcc)下面和Windows(cl)下所處的C Library目錄不一樣。包含的處理辦法就不同。可能須要這樣寫才能徹底正確的包含。

#if HAVE_UTIME_H ---- 若是有utime.h 文件
# ifdef WIN32 -----若是是win32環境 
# include <sys/utime.h> -----包含sys/utime.h
# endif
# ifdef LINUX ---- 若是是Linux環境
# include <utime.h> ---- 包含utime.h 
# endif
#else --- 若是沒有utime.h定義出須要的結構 
struct utimbuf
{
long actime;
long modtime;
};
#endif

  在其餘的代碼中基本上也是這樣的處理。根據編譯環境的不一樣來編譯不一樣的代碼。 這樣的define的區隔,主要就是爲了區隔不一樣平臺的不一樣細微區別。有的區別也許是某些常量沒有定義,有些區別是某些函數不存在。若是代碼中調用函數在某些平臺下面不存在,就須要提供一個lib去提供這些函數。Tar的Lib的做用也是如此。

  基本上代碼的移植是前難後易。前期首先要保證源代碼自己的邏輯不能變更,因此在修改代碼方面只能儘可能修改外圍的代碼,而不是修改源代碼自己。若是link過了以後,則就是通常的Windows下面的編程了,能夠根據需求任意修改移植後的代碼了。最難的地方可能就是OS級不一樣概念的替換了。C Library雖然在各個平臺上有不一樣之處,可是老是比較接近,不一樣的地方能夠提供本身編寫的代碼來替換。可是OS級的概念,和平臺相關性太大,通常不太容易替換。

  六:擴展問題,待解決的問題

  若是須要把移植過來的代碼改爲DLL或者lib給其餘的工程調用。好比給其餘的工程提供一個解包Tar文件的功能。若是不加修改,那麼移植過來的代碼有不少缺陷。

  首先是多線程支持問題。若是代碼中有不少全局變量,那麼改爲DLL或者lib以後就不能在多線程下面調用。

  其次,DLL接口表。移植後的代碼入口是main函數,雖然整個工程裏面有不少獨立功能,可是這些獨立功能的調用都是經過使用不一樣的參數來實現。如何輸出接口表給其餘工程使用,須要作些功夫。

  3、控制 原始的控制檯程序在下了運行參數以後,通常都是一頭運行到底的,也有可能在中間有些要求輸入某些信息的。這樣的程序如何集成到其餘的工程中並受到其餘工程的控制?好比遇到某些錯誤要返回等等。在Tar代碼中遇到錯誤就直接退出程序。顯然這些地方就不合DLL設計要求。可能須要從新設計代碼的結構。

  四,輸出信息。Tar工程裏面不少向控制檯輸出的信息。這些信息輸出須要從新定向或者屏蔽。

  第三第四部分能夠參考Linux下面的FrontEnd程序,即只是爲某個特殊的程序提供的一個GUI界面的程序。FrontEnd程序就是控制了主程序的運行並從新定向輸出信息到GUI界面上。

  注1. Cygwin,是Windows平臺下面的一個Linux模擬環境。能夠從www.Cygwin.com上下載所有內容。

  注2. Windows Services for UNIX (SFU)的SDK能夠從微軟網站上得到 http://www.microsoft.com/windows/sfu/

  注3. UNIX Application Migration Guide 能夠從MSDN中取得,若是沒有MSDN能夠從微軟MSDN網站上取得。 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnucmg/html/ucmglp.asp

  注4. Tar, Cygwin下面有Tar。可是隻能在Cygwin下面運行 或者必須提供Cygwin的平臺DLL才能在windows下面單獨使用Tar程序。
注5. CL是微軟的C/C++編譯器,包含在Visual Studio各個版本中

  本文於2003年完成. 如須要轉載 請聯繫jackforce@163.com,若是有看到部分干擾信息.請原諒.主要避免轉載過程當中做者信息丟失用.不得覺得之,請各位原諒.

  PS :

  用一個例子簡單說明了從linux平臺移植到windows平臺上的一些須要注意的問題和解決方法.

  例子僅用來講明移植過程產生的問題用.

相關文章
相關標籤/搜索