ひまlab

ひまな時になんか書く。

Android NDKで、Boost 1.52.0を使うために

Androidで、Boost 1.52.0を使う機会があったので、ビルド手順を記録します。

環境

資材

手順

1. CygwinとNDKをセットアップ
2. boostをダウンロード
3. 任意の場所に解凍

    • ここでは、c:\work\boost_1_52_0 を仮定

4. パッチ(boost_1_52_0.patch)をc:\work\を配置して、次のコマンドを実行する

 $ c:\work
 $ patch -p0 boost_1_52_0.patch

5. boost_1_52_0\tools\build\v2\user-config.jamのANDROID_NDK_ROOTを次のように修正

ANDROID_NDK_ROOT = <NDKの配置先>

6. ビルドする

 $ c:\work\boost_1_52_0
 $ ./bootstrap.sh
 $ ./b2 toolset=gcc-android4.6 link=static runtime-link=static target-os=linux –stagedir=android

7. ライブラリが出来上がる

パッチ

boost_1_52_0.patchの中身

diff -ur boost_1_52_0/boost/asio/detail/fenced_block.hpp boost_1_52_0_patched/boost/asio/detail/fenced_block.hpp
--- boost_1_52_0/boost/asio/detail/fenced_block.hpp	2012-07-23 04:03:30.000000000 +0900
+++ boost_1_52_0_patched/boost/asio/detail/fenced_block.hpp	2012-11-21 13:05:11.949629346 +0900
@@ -25,7 +25,7 @@
 # include <boost/asio/detail/macos_fenced_block.hpp>
 #elif defined(__sun)
 # include <boost/asio/detail/solaris_fenced_block.hpp>
-#elif defined(__GNUC__) && defined(__arm__) \
+#elif defined(__GNUC__) && defined(__arm__) && !defined(__thumb__) \
   && !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)
 # include <boost/asio/detail/gcc_arm_fenced_block.hpp>
 #elif defined(__GNUC__) && (defined(__hppa) || defined(__hppa__))
@@ -35,7 +35,8 @@
 #elif defined(__GNUC__) \
   && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) \
   && !defined(__INTEL_COMPILER) && !defined(__ICL) \
-  && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__)
+  && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__) \
+  && !defined(ANDROID) && !defined(__ANDROID__)
 # include <boost/asio/detail/gcc_sync_fenced_block.hpp>
 #elif defined(BOOST_WINDOWS) && !defined(UNDER_CE)
 # include <boost/asio/detail/win_fenced_block.hpp>
@@ -55,7 +56,7 @@
 typedef macos_fenced_block fenced_block;
 #elif defined(__sun)
 typedef solaris_fenced_block fenced_block;
-#elif defined(__GNUC__) && defined(__arm__) \
+#elif defined(__GNUC__) && defined(__arm__) && !defined(__thumb__) \
   && !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)
 typedef gcc_arm_fenced_block fenced_block;
 #elif defined(__GNUC__) && (defined(__hppa) || defined(__hppa__))
@@ -65,7 +66,8 @@
 #elif defined(__GNUC__) \
   && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) \
   && !defined(__INTEL_COMPILER) && !defined(__ICL) \
-  && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__)
+  && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__) \
+  && !defined(ANDROID) && !defined(__ANDROID__)
 typedef gcc_sync_fenced_block fenced_block;
 #elif defined(BOOST_WINDOWS) && !defined(UNDER_CE)
 typedef win_fenced_block fenced_block;
diff -ur boost_1_52_0/boost/asio/ip/address_v6.hpp boost_1_52_0_patched/boost/asio/ip/address_v6.hpp
--- boost_1_52_0/boost/asio/ip/address_v6.hpp	2012-01-15 05:46:24.000000000 +0900
+++ boost_1_52_0_patched/boost/asio/ip/address_v6.hpp	2012-11-21 13:16:18.257708064 +0900
@@ -11,6 +11,23 @@
 #ifndef BOOST_ASIO_IP_ADDRESS_V6_HPP
 #define BOOST_ASIO_IP_ADDRESS_V6_HPP
 
+// @Moss - Define IPv6 macros
+#if !defined(IN6_IS_ADDR_MULTICAST) 
+#define IN6_IS_ADDR_MULTICAST(a) (((__const uint8_t *) (a))[0] == 0xff)
+#endif
+
+#if !defined(IN6_IS_ADDR_MC_NODELOCAL)
+#define IN6_IS_ADDR_MC_NODELOCAL(a) \
+        (IN6_IS_ADDR_MULTICAST(a) \
+         && ((((__const uint8_t *) (a))[1] & 0xf) == 0x1))
+#endif
+
+#if !defined(IN6_IS_ADDR_MC_GLOBAL)
+#define IN6_IS_ADDR_MC_GLOBAL(a) \
+        (IN6_IS_ADDR_MULTICAST(a) \
+         && ((((__const uint8_t *) (a))[1] & 0xf) == 0xe))
+#endif
+
 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
 # pragma once
 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
diff -ur boost_1_52_0/boost/config/user.hpp boost_1_52_0_patched/boost/config/user.hpp
--- boost_1_52_0/boost/config/user.hpp	2004-01-10 04:10:00.000000000 +0900
+++ boost_1_52_0_patched/boost/config/user.hpp	2012-11-21 13:19:15.433681664 +0900
@@ -12,6 +12,9 @@
 //  Use this file to define a site and compiler specific
 //  configuration policy:
 //
+#define __arm__ 1
+#define _REENTRANT 1
+#define _GLIBCXX__PTHREADS 1
 
 // define this to locate a compiler config file:
 // #define BOOST_COMPILER_CONFIG <myheader>
diff -ur boost_1_52_0/boost/detail/endian.hpp boost_1_52_0_patched/boost/detail/endian.hpp
--- boost_1_52_0/boost/detail/endian.hpp	2011-03-29 14:58:48.000000000 +0900
+++ boost_1_52_0_patched/boost/detail/endian.hpp	2012-11-21 13:20:17.925611353 +0900
@@ -31,7 +31,7 @@
 // GNU libc offers the helpful header <endian.h> which defines
 // __BYTE_ORDER
 
-#if defined (__GLIBC__)
+#if defined (__GLIBC__) || defined(ANDROID)
 # include <endian.h>
 # if (__BYTE_ORDER == __LITTLE_ENDIAN)
 #  define BOOST_LITTLE_ENDIAN
diff -ur boost_1_52_0/boost/interprocess/detail/workaround.hpp boost_1_52_0_patched/boost/interprocess/detail/workaround.hpp
--- boost_1_52_0/boost/interprocess/detail/workaround.hpp	2012-09-24 05:17:34.000000000 +0900
+++ boost_1_52_0_patched/boost/interprocess/detail/workaround.hpp	2012-11-21 13:21:17.853633729 +0900
@@ -54,7 +54,7 @@
    #endif
 
    //Check for XSI shared memory objects. They are available in nearly all UNIX platforms
-   #if !defined(__QNXNTO__)
+   #if !defined(__QNXNTO__) && !defined(ANDROID)
       #define BOOST_INTERPROCESS_XSI_SHARED_MEMORY_OBJECTS
    #endif
 
diff -ur boost_1_52_0/libs/filesystem/src/operations.cpp boost_1_52_0_patched/libs/filesystem/src/operations.cpp
--- boost_1_52_0/libs/filesystem/src/operations.cpp	2012-08-13 05:49:12.000000000 +0900
+++ boost_1_52_0_patched/libs/filesystem/src/operations.cpp	2012-11-21 13:27:58.861594656 +0900
@@ -73,13 +73,15 @@
     const fs::path dot_dot_path("..");
 #   include <sys/types.h>
 #   include <sys/stat.h>
-#   if !defined(__APPLE__) && !defined(__OpenBSD__)
+#   if !defined(__APPLE__) && !defined(__OpenBSD__) && !defined(__ANDROID__) && !defined(ANDROID)
 #     include <sys/statvfs.h>
 #     define BOOST_STATVFS statvfs
 #     define BOOST_STATVFS_F_FRSIZE vfs.f_frsize
 #   else
 #     ifdef __OpenBSD__
 #     include <sys/param.h>
+#elif defined(__ANDROID__) || defined(ANDROID) // @Moss - Android messes up a bit with some headers, this one is the correct one :D
+#     include <sys/vfs.h>
 #     endif
 #     include <sys/mount.h>
 #     define BOOST_STATVFS statfs
@@ -206,7 +208,19 @@
          || ::mkdir(to.c_str(),from_stat.st_mode)!= 0))
 #   define BOOST_COPY_FILE(F,T,FailIfExistsBool)copy_file_api(F, T, FailIfExistsBool)
 #   define BOOST_MOVE_FILE(OLD,NEW)(::rename(OLD, NEW)== 0)
+#if defined(__ANDROID__) || defined(ANDROID)
+    int BOOST_RESIZE_FILE(const char *path, off_t size)
+    {
+      int result = -1;
+      int fd = open(path, O_WRONLY);
+      if (fd != -1)
+	    result = ftruncate(fd, size);
+      close(fd);
+      return result;
+    }
+#else
 #   define BOOST_RESIZE_FILE(P,SZ)(::truncate(P, SZ)== 0)
+#endif
 
 #   define BOOST_ERROR_NOT_SUPPORTED ENOSYS
 #   define BOOST_ERROR_ALREADY_EXISTS EEXIST
@@ -1906,7 +1920,11 @@
     if (max == 0)
     {
       errno = 0;
+#     ifdef __ANDROID__ || ANDROID
+      long tmp = 4096;
+#     else
       long tmp = ::pathconf("/", _PC_NAME_MAX);
+#     endif
       if (tmp < 0)
       {
         if (errno == 0)// indeterminate
diff -ur boost_1_52_0/tools/build/v2/user-config.jam boost_1_52_0_patched/tools/build/v2/user-config.jam
--- boost_1_52_0/tools/build/v2/user-config.jam	2011-01-25 10:06:12.000000000 +0900
+++ boost_1_52_0_patched/tools/build/v2/user-config.jam	2012-11-21 13:34:51.893594276 +0900
@@ -90,3 +90,54 @@
 
 # Configure specific Python version.
 # using python : 3.1 : /usr/bin/python3 : /usr/include/python3.1 : /usr/lib ;
+##################################################
+#
+#  android
+#
+##################################################
+import os ;
+
+if [ os.name ] = CYGWIN || [ os.name ] = NT
+{
+        androidPlatform = windows ;
+}
+
+else if [ os.name ] = LINUX
+{
+        androidPlatform = linux-x86 ;
+}
+
+else if [ os.name ] = MACOSX
+{
+        androidPlatform = darwin-x86 ;
+
+}
+ANDROID_NDK_ROOT = c:/android/ndk-r8 ;
+modules.poke : NO_BZIP2 : 1 ; 
+
+using gcc : android4.6 :
+    $(ANDROID_NDK_ROOT)/toolchains/arm-linux-androideabi-4.6/prebuilt/$(androidPlatform)/bin/arm-linux-androideabi-g++ :
+    <archiver>$(ANDROID_NDK_ROOT)/toolchains/arm-linux-androideabi-4.6/prebuilt/$(androidPlatform)/bin/arm-linux-androideabi-ar
+    <ranlib>$(ANDROID_NDK_ROOT)/toolchains/arm-linux-androideabi-4.6/prebuilt/$(androidPlatform)/bin/arm-linux-androideabi-ranlib
+    <compileflags>--sysroot=$(ANDROID_NDK_ROOT)/platforms/android-14/arch-arm
+    <compileflags>-I$(ANDROID_NDK_ROOT)/sources/cxx-stl/gnu-libstdc++/4.6/include
+    <compileflags>-I$(ANDROID_NDK_ROOT)/sources/cxx-stl/gnu-libstdc++/4.6/libs/armeabi/include
+    <compileflags>-mthumb
+    <compileflags>-fno-strict-aliasing
+    <compileflags>-Os
+    <compileflags>-O2
+    <compileflags>-DANDROID
+    <compileflags>-D__ANDROID__
+    <compileflags>-DGLIBC
+    <compileflags>-D_GLIBCPP_USE_WCHAR_T
+    <compileflags>-D_LITTLE_ENDIAN
+    <compileflags>-DNDEBUG
+    <compileflags>-g
+    <compileflags>-lstdc++
+    <architecture>arm
+#    <cxxflags>-DBOOST_THREAD_VERSION=3
+    <cxxflags>-DPAGE_SIZE=2048
+    <cxxflags>-D__arm__
+    <cxxflags>-D_REENTRANT
+        ;
+

オフライン環境のPython 3.3にJinja2を入れてみる[備忘録]

流行りに乗って、Python 3.3を使い始めてみました。
2系から結構変わってるところもあり、結構戸惑ってます。

そんな中で、オフライン環境にJinja2を入れようとして詰まったのでメモします。

環境と資材

手順

  1. distributeをダウンロード
  2. distribute-x.x.xx.tar.gzを解凍し、以下のコマンドを実行
 > cd distribute-0.6.28/
 > python setup.py install
  1. Jinja2をダウンロード
  2. 解凍し、以下のコマンドを実行
 > cd mitsuhiko-jinja2-xxxxxxx
 > python setup.py install
  • 試してみる
 > python
 >>> from jinja2 import Template
 >>> template = Template('Hello {{ name }}!')
 >>> template.render(name='Kanjin')
'Hello Kanjin!'

あとがき

Pythonのモジュール管理は、発展しているみたいです。
また、新しいPythonでは"packaging" モジュールが追加される(された?)話もあります。
ただ、僕はあまりその辺を追っかけてなかったので、そろそろ後追いしてみようと思います。

例外をフロー制御に使用すべきでない理由について

コーディングにおいて、例外をフロー制御に使用すべきでないと言われます。
ただ、なぜ使用すべきでないかがあまり語られない気がするので、考えてみました。

例外をフロー制御に使用した例(Java)

Javaでは、文字列を数値型に変換できるかを判定する仕組みが、標準では存在しない*1
そのため、以下の様な実装で判定することがある。

    // 文字列がintに変換可能か判定する。
    // true:変換できる, false: 変換できない
    private boolean isConvertibleToInt(String objective) {

    // intに変換し、例外が発生しなければ変換可能と判断する
        try {
            Integer.parseInt(objective);
        } catch (NumberFormatException e) {
            return false;
        }

        return true;
    }

例外をフロー制御に使用すべきでない理由

なぜかと考えた時に、行き着いた答えは次の2(+1)つ。

1. 例外が"例外"でなくなるため

例外が正常な処理の中で発生することになる。
そのため、正常なのに例外が発生する。
例外が"例外"でなくなり結果として、例外が無視されてしまう。

上の例では、「NumberFormatException」が発生しても問題がないため、たいてい無視される。

        try {
            Integer.parseInt(objective);
        } catch (NumberFormatException e) {
            return false;
        }

2. 処理結果が例外に依存するため

上の例では変換できないという結果が「NumberFormatException」が発生することに依存する。
これでは、「NumberFormatException」が別の例外に変わった時に正しく動かなくなってしまう。

  • たとえば、Integer.parseInt()にnullが渡された時に、NumberFormatExceptionでない例外が発生するようになるかもしれない。
    • その時は、文字通り"例外"で落ちることになるので、良いといえば良いのだけど・・。

+1. 例外が発生する場合のフローが、わかりにくい。

例外処理に飛ぶことが、コードに書かれていないため場合によってはGOTO以上に可読性を落とす。
例外とGOTOの違いが、(自分が)整理できていないため、詳しくは書けない。

*1:知らないだけかもしれない。Commonsを使うとか

Maven3で、GlassFish3を使ってみる

Oracle OpenWorld 2012で熱く語られていた「GlassFish」を使ってみる。
ただ使うのもあれなので、前から気になってたMaven3と組み合わせてスムーズな開発をしてみよう!
コンセプトは、簡単でクリーンな開発。

環境

  • OS: Windows 7(64bit)
  • APサーバ: GlassFish3.1.2
  • 管理ツール: maven 3.0.4
  • JDK: jdk1.6.0_29

準備(インストール)

GlassFish

  1. ここからダウンロード
    • インストーラ版でもいいけど、zip版を使用
  2. 解凍し、適当な場所に配置
  3. glassfish3\binにPATHを通す
  4. glassfish3\bin\asadminをリネーム(asadmin_bkなど)*1
    • asadmin.batはそのまま

maven

  1. ここからダウンロード
  2. 解凍し、適当な場所に配置
  3. apache-maven-3.0.4\binにPATHを通す
  4. プロキシ環境の人は、apache-maven-3.0.4\conf\settings.xmlをコメントアウト解除し、編集する。
    <proxy>
      <id>optional</id>
      <active>true</active>
      <protocol>http</protocol>
      <username>proxyuser</username>
      <password>proxypass</password>
      <host>proxy.host.net</host>
      <port>80</port>
      <nonProxyHosts>local.net|some.host.com</nonProxyHosts>
    </proxy>

JDK

  1. ダウンロードして、インストール
  2. インストール先をJAVA_HOMEに指定
  3. JAVA_HOEM\binにPATHを通す

GlassFishのドメイン作成

 > asadmin.bat create-domain my_domain
    • ユーザ名とパスワードを聞かれるので、入力する
      • ちなみに、これをやらなくても最初から「domain1」というドメインが作られています。
  • 確認
 > ./asadmin.bat list-domains
    • my_domainが出れば成功。

Mavenプロジェクトの作成

  • コマンドプロンプト立ちあげて、プロジェクトを作成したいディレクトリに移動する。
    • 例では、「C:\project」に作成する。
 > cd C:\project
  • mvnコマンドを実行し、プロジェクトを作成
    • webアプリを作成するために、archetypeArtifactIdに「maven-archetype-webapp」を指定する
    • groupIdはプロジェクトの階層、artifactIdはプロジェクトの識別子になる
 > mvn archetype:generate -DarchetypeArtifactId=maven-archetype-webapp -DgroupId=com.example -DartifactId=MyMavenApp
    • versionとパッケージを聞かれるが、デフォルト設定を選ぶためEnterを押す。
  • MyMavenAppディレクトリが作成される。
    • pom.xmlがあることを確認。
      • pom.xmlは、mavenプロジェクトに関する情報を記述するためのファイルです。
 > dir MyMavenApp
   pom.xml  src

GlassFishと連携

  • 作成したMyMavenAppをGlassFishにデプロイするための設定を行う
  • pom.xmlを編集する
<!-- 編集前-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>MyMavenApp</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>MyMavenApp Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <finalName>MyMavenApp</finalName>
  </build>
</project>
<!-- 編集後 -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>MyMavenApp</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>MyMavenApp Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <finalName>MyMavenApp</finalName>
    <plugins>
      <plugin>
        <groupId>org.glassfish.maven.plugin</groupId>
        <artifactId>maven-glassfish-plugin</artifactId>
        <version>2.1</version>
        <configuration>
          <!-- ドメイン作成時に指定したユーザ -->
          <user>admin</user>
          <!-- GlassFishのインストール先 -->
          <glassfishDirectory>C:/app/glassfish3</glassfishDirectory>
          <!-- ドメイン作成時に指定したパスワード -->
          <adminPassword>admin</adminPassword>
          <debug>true</debug>
          <echo>true</echo>
          <components>
          <component>
          <name>${project.artifactId}</name>
          <artifact>${project.build.directory}/${project.build.finalName}.war</artifact>
          </component>
          </components>
          <domain>
            <name>my_domain</name>
            <adminPort>4848</adminPort>
          </domain>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
  • my_domainを起動する
 > asadmin.bat start-domains my_domain
  • my_domainにMyMavenAppをデプロイする
 > cd C:\project\MyMavenApp
 > mvn war:war glassfish:deploy
  • MyMavenAppにアクセス
    • プラウザを起動し、URLを入力
 http://localhost:8080/MyMavenApp/

f:id:kanjin:20120421105708p:plain

これで、開発の準備が整いました。

終わり

*1:これをやらないとmaven3のバグ?で連携がうまくできない http://stackoverflow.com/questions/6525979/maven-glass-fish-plug-in-problem