OpenJDKをビルドしてみた

デバッグ版のJVMが欲しかったのでビルドしてみた。

環境はMac

基本ソースをMercurialでクローンしてREADME読めばできる。

READMEにもあるけど、詳しいビルド方法はここを参照すればいい。
http://hg.openjdk.java.net/jdk8/jdk8/raw-file/tip/README-builds.html

以下、作業手順。

デバッグ版が欲しい場合は Configure時に--enable-debug するとよい。

% hg clone http://hg.openjdk.java.net/jdk8/jdk8 jdk8_src
requesting all changes
adding changesets
adding manifests
adding file changes
added 942 changesets with 1309 changes to 136 files
updating to branch default
82 files updated, 0 files merged, 0 files removed, 0 files unresolved

% cd jdk8_src
% ./get_source.sh
# Repositories:  corba jaxp jaxws langtools jdk hotspot nashorn

                corba:   hg clone http://hg.openjdk.java.net/jdk8/jdk8/corba corba
                 jaxp:   hg clone http://hg.openjdk.java.net/jdk8/jdk8/jaxp jaxp
                 jaxp:   requesting all changes
                corba:   requesting all changes
                corba:   adding changesets
                 jaxp:   adding changesets
Waiting 5 secs before spawning next background command.
                 jaxp:   adding manifests
                corba:   adding manifests
                jaxws:   hg clone http://hg.openjdk.java.net/jdk8/jdk8/jaxws jaxws
            langtools:   hg clone http://hg.openjdk.java.net/jdk8/jdk8/langtools langtools
                jaxws:   requesting all changes
            langtools:   requesting all changes
                jaxws:   adding changesets
            langtools:   adding changesets

〜 中略 〜
                    .:   cd . && hg pull -u
              ./corba:   cd ./corba && hg pull -u
            ./hotspot:   cd ./hotspot && hg pull -u
               ./jaxp:   cd ./jaxp && hg pull -u
              ./jaxws:   cd ./jaxws && hg pull -u
                ./jdk:   cd ./jdk && hg pull -u
          ./langtools:   cd ./langtools && hg pull -u
            ./nashorn:   cd ./nashorn && hg pull -u
            ./hotspot:   pulling from http://hg.openjdk.java.net/jdk8/jdk8/hotspot
            ./nashorn:   pulling from http://hg.openjdk.java.net/jdk8/jdk8/nashorn
              ./jaxws:   pulling from http://hg.openjdk.java.net/jdk8/jdk8/jaxws
               ./jaxp:   pulling from http://hg.openjdk.java.net/jdk8/jdk8/jaxp
          ./langtools:   pulling from http://hg.openjdk.java.net/jdk8/jdk8/langtools
                    .:   pulling from http://hg.openjdk.java.net/jdk8/jdk8
              ./corba:   pulling from http://hg.openjdk.java.net/jdk8/jdk8/corba
                ./jdk:   pulling from http://hg.openjdk.java.net/jdk8/jdk8/jdk
            ./hotspot:   searching for changes
            ./hotspot:   no changes found
              ./jaxws:   searching for changes
              ./jaxws:   no changes found
               ./jaxp:   searching for changes
               ./jaxp:   no changes found
            ./nashorn:   searching for changes
            ./nashorn:   no changes found
          ./langtools:   searching for changes
          ./langtools:   no changes found
              ./corba:   searching for changes
              ./corba:   no changes found
                    .:   searching for changes
                    .:   no changes found
                ./jdk:   searching for changes
                ./jdk:   no changes found

% bash ./configure --enable-debug --with-target-bits=64
Running generated-configure.sh
configure: Configuration created at Sat Oct  4 01:07:52 JST 2014.
configure: configure script generated at timestamp 1389186094.
checking for basename... /usr/bin/basename
checking for bash... /bin/bash
checking for cat... /bin/cat
checking for chmod... /bin/chmod
checking for cmp... /usr/bin/cmp

〜 中略 〜

checking if build directory is on local disk... yes
configure: creating /Users/plugram/Desktop/jvm/jdk8_src/build/macosx-x86_64-normal-server-fastdebug/config.status
config.status: creating /Users/plugram/Desktop/jvm/jdk8_src/build/macosx-x86_64-normal-server-fastdebug/spec.gmk
config.status: creating /Users/plugram/Desktop/jvm/jdk8_src/build/macosx-x86_64-normal-server-fastdebug/hotspot-spec.gmk
config.status: creating /Users/plugram/Desktop/jvm/jdk8_src/build/macosx-x86_64-normal-server-fastdebug/bootcycle-spec.gmk
config.status: creating /Users/plugram/Desktop/jvm/jdk8_src/build/macosx-x86_64-normal-server-fastdebug/compare.sh
config.status: creating /Users/plugram/Desktop/jvm/jdk8_src/build/macosx-x86_64-normal-server-fastdebug/spec.sh
config.status: creating /Users/plugram/Desktop/jvm/jdk8_src/build/macosx-x86_64-normal-server-fastdebug/Makefile
config.status: creating /Users/plugram/Desktop/jvm/jdk8_src/build/macosx-x86_64-normal-server-fastdebug/config.h

====================================================
A new configuration has been successfully created in
/Users/plugram/Desktop/jvm/jdk8_src/build/macosx-x86_64-normal-server-fastdebug
using configure arguments '--enable-debug --with-target-bits=64'.

Configuration summary:
* Debug level:    fastdebug
* JDK variant:    normal
* JVM variants:   server
* OpenJDK target: OS: macosx, CPU architecture: x86, address length: 64

Tools summary:
* Boot JDK:       java version "1.8.0_20" Java(TM) SE Runtime Environment (build 1.8.0_20-b26) Java HotSpot(TM) 64-Bit Server VM (build 25.20-b23, mixed mode)  (at /Library/Java/JavaVirtualMachines/jdk1.8.0_20.jdk/Contents/Home)
* C Compiler:     i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build) version 2335.15.00) (at /usr/llvm-gcc-4.2/bin/llvm-gcc-4.2)
* C++ Compiler:   i686-apple-darwin11-llvm-g++-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build) version 2335.15.00) (at /usr/llvm-gcc-4.2/bin/llvm-g++-4.2)

Build performance summary:
* Cores to use:   2
* Memory limit:   4096 MB
* ccache status:  not installed (consider installing)

Build performance tip: ccache gives a tremendous speedup for C++ recompilations.
You do not have ccache installed. Try installing it.


% make all
Building OpenJDK for target 'all' in configuration 'macosx-x86_64-normal-server-fastdebug'

## Starting langtools
Compiling 2 files for BUILD_TOOLS
Compiling 31 properties into resource bundles
Compiling 777 files for BUILD_BOOTSTRAP_LANGTOOLS
Creating langtools/dist/bootstrap/lib/javac.jar
Updating langtools/dist/lib/src.zip
Compiling 780 files for BUILD_FULL_JAVAC
Creating langtools/dist/lib/classes.jar
## Finished langtools (build time 00:01:02)

## Starting hotspot
make[2]: warning: -jN forced in submake: disabling jobserver mode.
INFO: ENABLE_FULL_DEBUG_SYMBOLS=1
INFO: ENABLE_FULL_DEBUG_SYMBOLS=1
INFO: ENABLE_FULL_DEBUG_SYMBOLS=1
INFO: ENABLE_FULL_DEBUG_SYMBOLS=1
INFO: ENABLE_FULL_DEBUG_SYMBOLS=1
Creating Makefile ...
Creating directory list ../shared_dirs.lst
Creating flags.make ...
Creating flags_vm.make ...
Creating vm.make ...
Creating adlc.make ...
Creating jvmti.make ...
Creating trace.make ...
Creating sa.make ...
Creating dtrace.make ...
INFO: ENABLE_FULL_DEBUG_SYMBOLS=1
Creating Makefile ...
Creating flags.make ...
Creating flags_vm.make ...
Creating vm.make ...
Creating adlc.make ...
Creating jvmti.make ...
Creating trace.make ...

〜 中略 〜

## Finished docs (build time 00:03:48)

----- Build times -------
Start 2014-10-04 01:10:54
End   2014-10-04 01:39:25
00:00:50 corba
00:01:00 demos
00:03:48 docs
00:12:07 hotspot
00:01:27 images
00:00:30 jaxp
00:00:44 jaxws
00:06:44 jdk
00:01:02 langtools
00:00:18 nashorn
00:28:31 TOTAL
-------------------------
Finished building OpenJDK for target 'all'

% make install
Building OpenJDK for target 'install' in configuration 'macosx-x86_64-normal-server-fastdebug'

## Starting langtools
## Finished langtools (build time 00:00:01)

## Starting hotspot
## Finished hotspot (build time 00:00:00)

## Starting corba
## Finished corba (build time 00:00:00)

## Starting jaxp
## Finished jaxp (build time 00:00:01)

## Starting jaxws
## Finished jaxws (build time 00:00:00)

## Starting jdk
## Finished jdk (build time 00:00:03)

## Starting demos
## Finished demos (build time 00:00:00)

## Starting nashorn
## Finished nashorn (build time 00:00:00)

## Starting images
Copying images/j2re-bundle/jre1.8.0.jre/Contents/Home/release
Copying images/j2sdk-bundle/jdk1.8.0.jdk/Contents/Home/.DS_Store
Copying images/j2sdk-bundle/jdk1.8.0.jdk/Contents/Home/jre
Copying images/j2sdk-bundle/jdk1.8.0.jdk/Contents/Home/jre/.DS_Store
Copying images/j2sdk-bundle/jdk1.8.0.jdk/Contents/Home/release
## Finished images (build time 00:00:04)

## Starting install
Installing jdk image into /usr/local/jvm/openjdk-1.8.0-internal-fastdebug
and creating 40 links from /usr/local/bin into the jdk.
## Finished install (build time 00:00:04)

----- Build times -------
Start 2014-10-04 01:54:51
End   2014-10-04 01:55:04
00:00:00 corba
00:00:00 demos
00:00:00 hotspot
00:00:04 images
00:00:04 install
00:00:01 jaxp
00:00:00 jaxws
00:00:03 jdk
00:00:01 langtools
00:00:00 nashorn
00:00:13 TOTAL
-------------------------


インストールしたら /usr/local/bin に入った。

さっそく実験。

以下のようなソースを書いて実行。

class Hello {
  public static void main(String[] args) {
    new Hello().hello();
  }

  public int hello() {
    int result = 1 + 2;
    return result;
  }
}
% /usr/local/bin/javac Hello.java
% /usr/local/bin/java -client -Xcomp -XX:+PrintIRWithLIR -XX:CompileOnly="Hello.hello" Hello
__bci__use__tid____instr____________________________________
. 0    0     4     B1 [0, 0] -> B2 sux: B2

     label [label:0xec065838]
. 0    0     7     std entry B2
     std_entry
     move [rsi|L] [R177|L]
     move [metadata:0xa1684420|M] [R178|M]
     move [Base:[R178|M] Disp: 108|I] [R179|I]
     add [R179|I] [int:8|I] [R179|I]
     move [R179|I] [Base:[R178|M] Disp: 108|I]
     move [metadata:0xa16842f0|M] [R180|M]
     logic_and [R179|I] [int:0|I] [R179|I]
     cmp [R179|I] [int:0|I]
     branch [EQ] [CounterOverflowStub: 0xec067718]
     label [label:0xec067740]
     branch [AL] [B2]

__bci__use__tid____instr____________________________________
. 0    0     5     B2 (S) [0, 0] -> B0 dom B1 sux: B0 pred: B1

     label [label:0xec065b48]
. 0    0     6     goto B0
     branch [AL] [B0]

__bci__use__tid____instr____________________________________
. 0    0     0     B0 (SV) [0, 3] dom B2 pred: B2

     label [label:0xec064e38]
. 3    0    i3     ireturn i2
     move [int:3|I] [rax|I]
     return [rax|I]

__bci__use__tid____instr____________________________________
. 0    0     4     B1 [0, 0] -> B0 sux: B0

     label [label:0xec8a8a38]
. 0    0     5     std entry B0
     std_entry
     move [rsi|L] [R177|L]
     branch [AL] [B0]

__bci__use__tid____instr____________________________________
. 0    0     0     B0 (SV) [0, 3] dom B1 pred: B1

     label [label:0xec8a8038]
. 3    0    i3     ireturn i2
     move [int:3|I] [rax|I]
     return [rax|I]

とりあえず今日はここまで。

友達から「これ無料で作ってよ」って言われたときに注意したいこと

最近あまりにもひどいので、今後同じような経験をしないよう備忘録として書いておきます。

プログラマとかデザイナーの人は一度は必ず経験があると思うのですが、
友人から「こんなアイデアがあるんだけど」「うちの会社のサイトがダサくてね」という切り出しで、
「"ちょっと"やってくれない?」って頼まれることがあります。


この"ちょっと"が曲者で、

・"ちょっと" だからお金を払わなくていいよね?
・"ちょっと" だからまた頼むね


って感じで友人という立場上、依頼者は頼みやすいので
最初は自分も特に嫌ではなく、やってしまうことがあります。


ですが、この次の "ちょっと" を "何となく" やってしまったため、
次もまた次も頼まれることになります。

こうなってくると、最初は無料で感謝してた友人の依頼者も
なぜか「当たり前にやってくれる」ものとして依頼してきて、
こちらがやりたいと思わないことも要求してきます。


こういうパターンを何回か経験しているので、いい加減自分も学習しろと思うのですが、
こうなってくるともう相手は自分のやりたいと思っていることも聞いてくれず、
また自分的にも単なる作業にしかならない仕事を休日を使ってやることになります。


土日作業し休みを費やしてやっても感謝されず(実際はありがとうの一言ぐらいは言ってもらえる)
一文も儲からないし技術的にもやりたいこととは違うので、やりたくないと思い始めるのですが、
問題はここで「やめる」といっても、依頼者は自分にはできないので「困る」としか言えません。


なので、とりあえず引き継ぐ方向性を探るのですが、そもそも無料でこんなことやりたい人なんかいないので、もちろん誰も見つかりません。

こうなってくるとその友人に会うのすら苦痛になり、友人関係も崩壊します。



そんな経緯が度々あったので、今後誰に頼まれても「無料で」なにかのアプリとかサイトを作るつもりは一切ありません。

勉強会で発表するのは続けたいですが、あくまで「自分がやりたいこと」と「終了期限や終了条件」が明確であることが前提です。


たぶん同じような状況になったことのある人も結構いると思うし、
現在こんな感じの状況の人も結構いると思う。


なのでそういう状況でいま困っている人は最初に「無料」でやる仕事の「終了条件」を
必ず依頼者と合意しておくことをお勧めします。


あと、アプリやサイトを無料で依頼してる人は「タダより高いものはない」ということ、
「アプリを作る」「サイトを運営する」には一定以上の覚悟と投資がないとできないと思っておいたほうがよいと思います。

PhpStormでisset()を使う時の注意点

ちょっとした凡ミスを防ぐためのメモ書きです。

PhpStormは(多分)他のどのPHPエディタ・IDEより賢いので、未定義な変数があるとちゃんとエラーをハイライトして「ここは使ってないよ」って教えてくれます。


ただ、PHPには元々 isset()っていう未定義かどうか判断できるメソッドがあり、
if条件文にこれを使ってしまうとPhpStormのエラーハイライトが効かない場合があります。

例えばこんな場合、
f:id:tan_go238:20140810141643p:plain

13行目のように直接 isset() を使わずラップしたメソッドを使用すればエラーはハイライト表示されるのですが、

9行目のように癖で直接 isset() を使ってしまうと当たり前ですがエラーはハイライトしません。
※ここを && ではなく || にするとエラーはハイライトされます。


結論

PhpStorm使う場合は、無意識のうちにエラーチェックをIDEに依存してしまっているので、
できるだけissetなどの未定義チェックはラップしたメソッドで行なうようにしましょう。

CakePHP2でFixtureのデータをCSVから任意のタイミングで変更する

CakePHPのFixtureは個人的に使い辛いと常々感じていて、理由としては

  • 1Fixtureクラスに対して1データしか用意できない
  • 状況によりデータを変えたい場合1Modelに対して複数のFixtureクラスを用意する必要がある

といったことなどから基本CakePHPではできるだけFixtureを使わずモックで済ませてきました。

ですが列数が多いDBのデータをモックで返すことや、DBも参照してテストしたい場面があったので、CSVからFixtureのデータを読み込むのをさくっと作ってみました。

この方法以外にも、PHPUnit_Extensions_Database_DataSet_CsvDataSet使うとか Fabricate 使うとか状況によって色々あるかと覆いますが参考までにコード載せときます。

app/Test/AppTestCase.php

<?php
App::uses('FileUtil', 'Utility');
App::uses('CakeTestFixture', 'TestSuite/Fixture');

class AppTestCase extends CakeTestCase
{

	/**
	 * CSVファイルを読込んでファイル内容を返します。<br>
	 * CSVファイルは実行するテストクラスと同じディレクトリに配置します。<br>
	 * ※1.CSVファイルのファイルサイズは1MBまで<br>
	 * ※2.CSVファイルの文字コーディングはShift_JIS<br>
	 * ※3.ヘッダー行には列名が必要<br>
	 *
	 * @param string ファイル名
	 * @return array 配列の配列を返します
	 */
	protected function readCsv($filename)
	{
		$class = new ReflectionClass($this);
		$testPath = pathinfo($class->getFileName());
		$csv = $testPath['dirname'] . DS . $filename;

		if (!file_exists($csv)) {
			throw new CakeException("Could not find CSV file. [$csv]");
		}

		$size = filesize($csv);
		if ($size > 1048576) {
			// 最大1MBまで
			throw new CakeException("CSV File is over maximum size. [$csv]-[$size]");
		}

		$data = array();
		$fp = fopen($csv, "r");
		// ヘッダー行取得
		$header = fgetcsv($fp);
		while ($row = fgetcsv($fp)) {
			mb_convert_variables('UTF-8', 'SJIS-win', $row);
			$array = array();
			foreach ($header as $index => $column) {
				if (array_key_exists($index, $row)) {
					$array[$column] = $row[$index] != 'NULL' ? $row[$index] : null;
				} else {
					$array[$column] = null;
				}
			}
			$data[] = $array;
		}
		return $data;
	}

	protected function loadCsvFixture($fixtureName, $filename)
	{
		$fixture = substr($fixtureName, strlen('app.'));
		$fixturePath = TESTS . 'Fixture';

		$className = Inflector::camelize($fixture);
		if (is_readable($fixturePath . DS . $className . 'Fixture.php')) {
			$fixtureFile = $fixturePath . DS . $className . 'Fixture.php';
			require_once $fixtureFile;
			$fixtureClass = $className . 'Fixture';
		} else {
			throw new CakeException('Could not read fixture class. ' . $fixtureName);
		}

		$fixture = new $fixtureClass();
		$data = $this->readCsv($filename);
		$fixture->records = $data;

		/** @var $fixtureManager AppFixtureManager */
		require_once TESTS . 'AppFixtureManager.php';
		$fixtureManager = new AppFixtureManager();
		$fixtureManager->loadSingle($fixture);
	}

	/**
	 * プライベートメソッドを実行します。
	 *
	 * @param object $obj インスタンス
	 * @param string $methodName メソッド名
	 * @return mixed 返り値
	 * @throws CakeException
	 */
	protected function invoke($obj, $methodName)
	{
		if (!isset($obj) || !is_object($obj)) {
			throw new CakeException('The parameter obj is not an object.');
		}
		$clazz = new ReflectionClass($obj);
		$method = $clazz->getMethod($methodName);
		$method->setAccessible(true);

		$countArgs = func_num_args() - 2;
		if ($countArgs > 0) {
			$array = array();
			$args = func_get_args();
			for ($i = 0; $i < $countArgs; $i++) {
				$index = $i + 2;
				$array[] = $args[$index];
			}
			$result = $method->invokeArgs($obj, $array);
		} else {
			$result = $method->invoke($obj);
		}
		return $result;
	}
} 


app/Test/AppFixtureManager.php

<?php
App::uses('CakeFixtureManager', 'TestSuite/Fixture');
App::uses('CakeTestFixture', 'TestSuite/Fixture');

class AppFixtureManager extends CakeFixtureManager
{
	/**
	 * @see CakeFixtureManager::loadSingle
	 */
	public function loadSingle(CakeTestFixture $fixture, $db = null, $dropTables = true)
	{
		if(isset($fixture)) {
			if (!$db) {
				$db = ConnectionManager::getDataSource($fixture->useDbConfig);
			}
			$this->_setupTable($fixture, $db, $dropTables);
			$fixture->truncate($db);
			$fixture->insert($db);
		} else {
			throw new UnexpectedValueException(__d('cake_dev', 'Referenced fixture class is not found'));
		}
	}
} 


使い方

  • AppTestCase.phpを継承したクラスを作成

class XxxTest extends AppTestCase {}

  • テストメソッド内で呼び出す

$this->loadCsvFixture('app.Xxx', Xxx.csv');

この場合、XxxTest.phpと同じディレクトリにXxx.csvを配置します。

Xxx.csvのファイル内容の1行目には列名、2行目以降にデータを入力しておきます。

loadCsvFixture実行時にCSVからデータを読込み、DBの値を更新します。

SourceTreeのマージツールにPhpStormを使う

去年ぐらいから仕事のリポジトリもGitに移行しています。
Subversionと比べるとGitが小回り効くのがすごく気に入ってるのですが、
たまにマージ時にコンフリクトが起こったときにPhpStormファイルが読みにくくなってちょっと不便でした。

他のグラフィカルにマージできるmergetool使えばいいっちゃいいのですが、
せっかくだからPhpStormでDiffとかマージもしたい。

色々調べたのですが、diffはできてもmergeできたという人がいない。。

Gistでやってそうな人を見つけたので試しにやってみたらできました。

https://gist.github.com/lvl-svasseur/7837523


設定は以下のようにします。
Macです

1. SourceTree -> 環境設定
2. Diff を選択
3. 外部Diff / Merge にそれぞれ以下の値を入力

差分表示ツール
 その他
Diffコマンド
 /Applications/PhpStorm.app/Contents/MacOS/phpstorm
引数
 diff $(cd $(dirname "$LOCAL") && pwd)/$(basename "$LOCAL") $(cd $(dirname "$REMOTE") && pwd)/$(basename "$REMOTE")


マージツール
 その他

Mergeコマンド
 /Applications/PhpStorm.app/Contents/MacOS/phpstorm
引数
 merge $(cd $(dirname "$LOCAL") && pwd)/$(basename "$LOCAL") $(cd $(dirname "$REMOTE") && pwd)/$(basename "$REMOTE") $(cd $(dirname "$BASE") && pwd)/$(basename "$BASE") $(cd $(dirname "$MERGED") && pwd)/$(basename "$MERGED")

f:id:tan_go238:20140224142710p:plain


いじょ。

2014年の抱負

明けてましたのでおめでとうございます。

今年は色々と慌ただしくなる感じがするので、抱負でも書いて初心を忘れないようにしておきたいと思います。

あんま色々やろうとしても無理なのは分かっているので心がけ程度ですが。


・仕事を丁寧にする

速度よりも時間がなくても丁寧な仕事をするように心がけたいです。

できるだけ使いやすいように。「仕様通り作った」とかいう言い訳は自他ともになしの方向で。


・万全を期して準備する

準備はしっかりしておく。準備しておかないと勝てるものも勝てないって去年やっと悟った。


・遊ぶ

やりたいと思ったことはやる。楽しむことが大事。



少ない感じですが以上かな。

今まで結構このへんのことが疎かになってたので、今年は気を引き締めていきたい。