いけむランド

はてダからやってきました

road to e17 (11)

去年末に e17 snapshot の cygwin 向け patch をつくった人から出るだろうと思われていた svn の trunk に対する patch が全然出てこないので、しびれを切らして、自分でつくることにした。


去年くらいからの Windows 対応のおかげで共有ライブラリやモジュールの拡張子を ".so" きめうちではなく、OS によって ".dll" にするような処理が既に入っているため、昔ほど手を加える必要はないが、そんな中でも発見できるノウハウはある。


たとえば eina のメモリアロケータモジュールに以下のようなコードがあった。

#ifdef _WIN32
# define MODULE_EXTENSION ".dll"
#else
# define MODULE_EXTENSION ".so"
#endif /* ! _WIN32 */


一見、cygwin では上の define が有効になりそうだが、実際は下の define が有効になり、そのままではモジュールをロードできない。_WIN32 というマクロは意外にも cygwin では定義されないのである。*1

ちなみにマクロ _WIN32 は gcc に -mno-cygwin オプションを指定した場合には定義される。以下に -mno-cygwin オプションの有無による定義済マクロの一覧の差分を示す。

% echo | gcc -v -E -dM             - > i686-pc-cygwin.log
% echo | gcc -v -E -dM -mno-cygwin - > i686-pc-mingw32.log
% diff <(grep define i686-pc-cygwin.log | sort) <(grep define i686-pc-mingw32.log | sort)
--- /dev/fd/63  (grep define i686-pc-cygwin.log | sort)
+++ /dev/fd/62  (grep define i686-pc-mingw32.log | sort)
@@ -1,7 +1,8 @@
+#define WIN32 1
+#define WINNT 1
+#define _WIN32 1
 #define _X86_ 1
 #define __CHAR_BIT__ 8
-#define __CYGWIN32__ 1
-#define __CYGWIN__ 1
 #define __DBL_DENORM_MIN__ 4.9406564584124654e-324
 #define __DBL_DIG__ 15
 #define __DBL_EPSILON__ 2.2204460492503131e-16
@@ -49,6 +50,8 @@
 #define __LDBL_MIN__ 3.36210314311209350626e-4932L
 #define __LONG_LONG_MAX__ 9223372036854775807LL
 #define __LONG_MAX__ 2147483647L
+#define __MINGW32__ 1
+#define __MSVCRT__ 1
 #define __NO_INLINE__ 1
 #define __PTRDIFF_TYPE__ int
 #define __REGISTER_PREFIX__ 
@@ -61,6 +64,8 @@
 #define __VERSION__ "3.4.4 (cygming special, gdc 0.12, using dmd 0.125)"
 #define __WCHAR_MAX__ 65535U
 #define __WCHAR_TYPE__ short unsigned int
+#define __WIN32 1
+#define __WIN32__ 1
 #define __WINT_TYPE__ unsigned int
 #define __cdecl __attribute__((__cdecl__))
 #define __declspec(x) __attribute__((x))
@@ -70,10 +75,7 @@
 #define __stdcall __attribute__((__stdcall__))
 #define __tune_i686__ 1
 #define __tune_pentiumpro__ 1
-#define __unix 1
-#define __unix__ 1
 #define _cdecl __attribute__((__cdecl__))
 #define _fastcall __attribute__((__fastcall__))
 #define _stdcall __attribute__((__stdcall__))
 #define i386 1
-#define unix 1


というわけで patch を ML に投げて、対応してもらった。

#if defined(_WIN32) || defined(__CYGWIN__)
# define MODULE_EXTENSION ".dll"
#else
# define MODULE_EXTENSION ".so"
#endif /* !defined(_WIN32) && !defined(__CYGWIN__) */


ところがもっとスマートな方法がある。それは libtool で変数 shrext_cmds にセットされている OS 毎の共有ライブラリおよびモジュールの拡張子を文字列としてソースコードに取り込んで、使うことである。この変数はたとえば、MacOSX (MacPorts) では ".dylib" になる (ただしモジュールは ".so") し、cygwin では ".dll" となる。

% uname
Darwin
% grep ^shrext_cmds /opt/local/bin/glibtool
shrext_cmds="\`test .\$module = .yes && echo .so || echo .dylib\`"
% uname
CYGWIN_NT-6.1
% grep ^shrext_cmds /usr/bin/libtool
shrext_cmds=".dll"


これを使うと、OS 毎の条件分岐が消えるため、新しい OS に対応する場合も libtool が移植されていれば、ソースコードを書き換える必要はなくなる。

--- src/lib/eina_module.c
+++ src/lib/eina_module.c
@@ -66,11 +66,7 @@
  * @cond LOCAL
  */
 
-#if defined(_WIN32) || defined(__CYGWIN__)
-# define MODULE_EXTENSION ".dll"
-#else
-# define MODULE_EXTENSION ".so"
-#endif /* !defined(_WIN32) && !defined(__CYGWIN__) */
+#define MODULE_EXTENSION SHARED_LIB_SUFFIX
 
 #define EINA_MODULE_SYMBOL_INIT "__eina_module_init"
 #define EINA_MODULE_SYMBOL_SHUTDOWN "__eina_module_shutdown"
--- configure.ac
+++ configure.ac
@@ -382,6 +382,9 @@
 EFL_CHECK_COVERAGE([${enable_tests}], [enable_coverage="yes"], 
[enable_coverage="no"])
 EFL_CHECK_BENCHMARK([enable_benchmark="yes"], [enable_benchmark="no"])
 
+# Checks for shared library suffix
+AC_DEFINE_UNQUOTED(SHARED_LIB_SUFFIX, "$shrext_cmds", [Suffix for shared objects])
+
 AC_CONFIG_FILES([
 Makefile
 eina-0.pc

*1:実際に patch を投げたら、committer から何故 Windows なのに、わざわざ修正が必要なんだ?という返事が来たが、すぐに別の人が説明をしたため、後述の通り無事に取り込まれた。