f

2014-12-29

gfortran has no include and library path envrironmental variable

gfortranでコンパイル時にインクルードパスやライブラリを指定する環境変数がないか調べた。gfortranにはインクルードパスやライブラリパスの環境変数が存在しないことがわかった。

Introduction

Fortranの自由なコンパイラにはいくつかある。その中でも安定していて将来性があるのはgfortranだ。そのため,自分でFortranプログラムを作るときはgfortranでコンパイルする。しかし,Fortranには標準ライブラリがないため,以下のどちらかの方法をとるしかない。

  • 低レベルな演算から自分で実装
  • 外部ライブラリに頼る

ある程度汎用的な演算などはモジュールやヘッダとしてまとめて使いまわせたほうが開発効率が高い。そのようにして分離されたモジュールやライブラリはコンパイル時にオプションでディレクトリを指定することで利用できる。しかし,コンパイルの度にいちいちディレクトリを指定するのは面倒だ。 Makefileに書き込んでおいてmakeでビルドする方法もある。大規模なプログラムならそれもありだが,たいした規模でないプログラムのためにいちいちMakefileを用意するのは煩雑だ。コマンドラインからコンパイルしたほうが楽だ。

自由なC/C+コンパイラであるgccg++であれば,に示した環境変数でインクルードファイル(ヘッダファイル)とライブラリのパスを指定できる。

GCCで使われるサーチパス環境変数
環境変数 説明
CPATH C言語のヘッダファイルのサーチパス
LD_LIBRARY_PATH ライブラリのサーチパス

gfortranもGCC(GNU Compiler Collection)の一部であるので同様の環境変数があってもおかしくない。そこで,これらの環境変数に該当するものがgfortranにもないか調査した。

環境変数の調査

調査にあたっては以下で公開されているGNU Fortranのマニュアルを参考にした。

GCC online documentation - GNU Project - Free Software Foundation (FSF) https://gcc.gnu.org/onlinedocs/

このgfortranのマニュアルの2.10 Environment Variables(https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gfortran/Environment-Variables.html#Environment-Variables )でgfortranの環境変数について以下のように言及されている。

The gfortran compiler currently does not make use of any environment variables to control its operation above and beyond those that affect the operation of gcc.

See Environment Variables Affecting GCC, for information on environment variables.

訳:gfortranは現在,上記(2.1~2.9節のコマンドオプション)とgccの動作に影響するものを超えて,動作を制御するために環境変数を使わない。

つまり,gfortranはコンパイラの動作の制御に環境変数を使わないようだ。次の3節にTMPDIRや既定の標準入力,出力,エラー出力の番号など実行時の環境変数が掲載されている。しかし,インクルードファイルやライブラリへのパスの環境変数は本当に存在しないようだ。

Runtime - The GNU Fortran Compiler https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gfortran/Runtime.html#Runtime

ちなみにIntel Fortran Compiler(ifort)など不自由なコンパイラなどではFPATHLD_LIBRARY_PATHなどでインクルードパスなどが実装されているようだ。

参考:https://software.intel.com/sites/default/files/m/f/8/5/8/0/6366-ifort.txt

個人的にはLD_LIBRARY_PATHであれば共通で使えるのではないかと思っていたが,無理のようで残念だ。自分でもINCLUDE変数などにパスを設定しコンパイルできるか試したが無理だった。

以上のことから,gfortranで外部ライブラリやモジュールを取り込む場合は面倒だがコンパイル時にフルパスで指定する必要がある。

コマンドラインからの実行

コンパイルオプションにより,インクルードやライブラリへのパスの指定方法は以下で説明されている。内容をにまとめた。

Directory Options - The GNU Fortran Compiler https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gfortran/Directory-Options.html#Directory-Options

gfortranでのコンパイル時のパス設定
オプション 説明
-I[dir] USE文(.modファイル)とINCLUDE文(.h,.f90ファイル)の検索ディレクトリ。
-J[dir] .modファイルの検索ディレクトリ。既定はカレントディレクトリ。
-L[dir] ライブラリの検索ディレクトリ。
-l[lib] ライブラリ(lib[lib].aなどの形式のファイル)名の指定。

この表の内容から,基本的には以下の書式でコンパイルすることになる。

gfortran [source1.f90 ... sourceN.f90]  -I/path/to/include -L/path/to/lib -l[lib]

例:

gfortran hi.f90 -I$HOME/local/f90/mod -L$HOME/local/lib -lposix90

この-I以降や-L以降にうまくCPATHLD_LIBRARY_PATHを当てて以下のようにできないか試してみた。

gfortran hi.f90 -I$CPATH -L$LD_LIBRARY_PATH -lposix90

うまくいかない。通常,CPATHLD_LIBRARY_PATHには複数のパスを:で区切って格納している。この:が原因でうまくパスが認識されない。複数のディレクトリを指定するときは確実にひとつずつ-I/path/to/include1 -I/path/to/include2 -L/path/to/libのように-Iや-Lを付けないと認識されない

私は$HOME/local/{include,lib}に,自分でインストールしたライブラリやインクルードを格納している。なので,LD_LIBRARY_PATHなども共通で使えたほうが忘れる心配がない。どうにかしてこれらの変数を使えないか試行錯誤した。その結果以下のようにすればCPATHLD_LIBRARY_PATHを使ってコンパイルできた。

# bash
gfortran hi.f90 -I${CPATH//:/ -I/} -L${LD_LIBRARY_PATH//:/ -L/} -lposix90
# zsh
gfortran hi.f90 -I${=CPATH//:/ -I/} -L${=LD_LIBRARY_PATH//:/ -L/} -lposix90

ここではCPATHLD_LIBRARY_PATHに含まれている:を全て -Iで置換している。zshだと空白も文字として扱われるので変数展開により対処している。これにより共通のCPATHLD_LIBRARY_PATHをうまく使ってコンパイルできる。

ただ,相変わらず面倒なことに変わりはない。ifortと同様にFPATH変数にFortranのインクルードやmodファイルのディレクトリを指定してコンパイル時にその都度指定したほうがわかりやすいだろう。

export FPATH=$HOME/local/f90/mod
gfortran hi.f90 -I$FPATH

Conclusion

今回の調査内容を以下にまとめた。

  • gfortranでインクルードファイルやライブラリのサーチパス変数がないか調べた。
  • gfortranのマニュアルからgfortranにはサーチパス変数が存在しないことがわかった。
  • 代替案として,以下二つの方法を示した。
    • CPATHLD_LIBRARY_PATHを展開して使う方法。
    • FPATH変数にインクルードファイルのディレクトリを指定して利用。

結論としては,gfortranにはサーチパス変数がないということであまりたいした成果はなかった。しかし,Fortranについてはネット上での情報が不足しているので,こうした情報も役に立つのではないかと考える。

現実的にはFPATH変数に自分のFortranモジュールの格納先を指定してコンパイル時に-I$FPATHで指定するのがよいだろう。

2014-12-28

Install Fortran 90 POSIX API (Posix90 library)

Fortran 90のPOSIXのAPIをインストールして,OSにアクセスが必要なディレクトリ操作や正規表現をFortranからでもできるようにする。

Introduction

Fortranを使っていると,プログラム内でディレクトリの作成・削除・移動,正規表現を利用したいことがある。しかし,これらはOSへのアクセスが必要であり標準ではできない。system関数で外部コマンドを呼びだせば実現可能なこともある。しかし,以下2点の問題がある。

  • 外部コマンドで取得した値をプログラム内で値を直接利用できない。
  • OSごとにコマンドが異なるため移植性・汎用性に欠ける。

外部コマンドで取得した値を利用する場合は中間ファイルを介せば理論的には可能だ。しかし,そのためだけにファイル読み書きのコードを書く必要があり冗長だ。ディレクトリを作成するmkdirコマンドに限れば,各OSに同名で存在するため利用可能だ。ただし,オプションはOSごとに異なるので使うと移植できない。例えば以下のようにして使う。

call system("mkdir hoge")

POSIX(Portable Operating System Interface)とは,UNIX系のOSでの最低限の共通APIの規格である。POSIXにアクセスすることでディレクトリ操作などをOSネイティブに実行できる。

FortranにおけるPOSIXへのAPIはISOで標準化されている。

IEEE (1992) : IEEE Standard for InformationTechnology - POSIX(R) FORTRAN 77 Language Interfaces - Part 1: Binding for System Application Program Interface (API) . http://standards.ieee.org/findstds/standard/1003.9-1992.html.

具体的な自由な実装としては以下二つがある。

FotranのPOSIXのモジュール
名前バージョン最終更新日URL
Posix90 1.1.1.1 2008-06-10 http://savannah.nongnu.org/projects
fortranposix 0.1-1 2005-08-17 http://sourceforge.net/projects/fortranposix

Posix90のバージョンは以下のコマンドで確認した。

cvs status -v

Posix90のほうが新しく実装が充実している。また,fortranposixはうまくインストールできなかった。Posix90をインストールしてFotranでディレクトリ作成できるようにしてみる。インストール環境は以下に示したとおりだ。

  • OS:Ubuntu 14.04
  • Cコンパイラ:gcc 4.8.2
  • Fortranコンパイラ:gfortran4.8.2

Method

コンパイルに先立って必要なパッケージをインストールしておく。

sudo apt install texinfo texlive texi2html cvs

上記のPosix90の配布元のDownloadから辿れるバージョン0.4は古くてインストールできなかったので,CVSを使ってリポジトリから最新バージョンを入手する。

cd ~/.local/src
cvs -d:pserver:anonymous@cvs.sv.gnu.org:/sources/posix90 co .
cd posix90
# sed -i.back '5cPREFIX := ${HOME}/local/stow/posix90-1.1.1.1' Makefile
vim Makefile # 5行目付近を修正
PREFIX := ${HOME}/.local/stow/posix90-1.1.1.1
#PREFIX := /usr/local
make

makeを実行すると以下のエラーが出る。

./gpl.texi:305: This command can appear only outside of any environment, not in
 environment @enumerate.

@badenverr ...temp , not @inenvironment @thisenv } @checkenv ...@ifx @thisenv @temp @else @badenverr @fi @sectionheading #1#2#3#4->{@checkenv {} @csname #2fonts@endcsname @rmisbold @... @\heading ...tionheading {#1}{sec}{Yomitfromtoc}{} @suppressfirstparagraphin... l.305 @heading NO WARRANTY ?

qを入力してとりあえず抜ける。doc/gpl.texiの305行目が原因。

@iftex
@heading NO WARRANTY
@end iftex

enumerate環境の中に見出しである@headingコマンドを書いているためエラーとなっている。該当行をコメントアウトしてmakeし直す。

sed -i.back '@comment @heading NO WARRANTY' doc/gpl.texi
make
make install
cd ~/.local/stow
stow posix90-1.1.1.1

これで~/.local/にインストールされた。マニュアルを読めるようにINFOPATHを設定しておく。

export INFOPATH=$HOME/.local/info

Test

とりあえず動くかどうかテストしてみる。

cd ~/tmp
cat <<- EOT > hi.f90
!! \file hi.f90
!! \brief 現在位置にhi-dirというディレクトリを作成。 use f90_unix_dir implicit none call mkdir("./hi-dir", 0) ! 第2引数でアクセス権などを指定 end
EOT

Posix90のモジュールとライブラリを指定してコンパイルする。

gfortran hi.f90 -I$HOME/.local/f90/mod -L$HOME/.local/lib -lposix90

ソースコード(hi.f90)をライブラリなど(-I, -L)より先に指定しないとうまくコンパイルできないので注意する。

コンパイルしてできたバイナリを以下のコマンドで実行するとhi-dirができる。

./a.out

How to use

すでにINFOPATHを設定しているので,以下のようにしてinfoコマンドでマニュアルを閲覧できる。

info posix90

ソースコードのディレクトリ(~/.local/src/posix90/doc)にmake実行時にhtmlやpdf形式でのマニュアルも生成されている。

配布元のソースコードリポジトリにtexi形式のマニュアルがある。

[posix90] View of /posix90/doc/posix90.texi

ただ,配布元はtexi形式でしか公開されておらず利便性が悪い。インストール前にどういう関数があるか確認できたほうがよいと思う。そこで,posix90のmakeのときに生成されたhtmlやpdf形式のマニュアル類を以下で公開した。

uploader/posix90 at master · lamsh/uploader

簡単にどういう関数があるか説明する。大まかに10種類のモジュールがある。

Posix90が提供するモジュールと機能
モジュール名説明
4.1 Module f90_unix_dir Directories and files
4.2 Module f90_unix_dirent Directory reading
4.3 Module f90_unix_env Environment
4.4 Module f90_unix_errno Error codes
4.5 Module f90_unix_file File characteristics
4.6 Module f90_unix_io Input/Output
4.7 Module f90_unix_proc Process management
4.8 Module f90_unix_regexp Regular expressions
4.9 Module f90_unix_signal Signal management
4.10 Module f90_unix_time Time types & conversion

個人的に特に有用だと思うのは4.1,4.2,4.8あたり。f90_unix_dirモジュールではディレクトリを作成したりできる。 f90_unix_direntモジュールでは。ディレクトリ以下から順番にファイル名を取得できる。f90_unix_regexpでは正規表現を使え るようだ。4.3のf90_unix_envは環境変数を取得したりできるが,これはgfortranの組み込み関数として同等の関数が存在するので使わなくても大丈夫だろう。

posix90モジュールを使うときは,hi.f90のように以下の手順を踏む。

  1. use文で該当する.modファイルを読み込む。
  2. call文でモジュールで定義されている関数を呼び出す。
  3. コンパイル時に.modファイルの場所とlibposix90.aのある場所を-I,-Lオプションで指定し,-lposix90を付与。

関数の使い方については,マニュアルを参照すればよい。

Conclusion

記事の内容をまとめる。

  • Fortran90でPOSIXのAPIにアクセスしてOSの機能を利用できるPosix90ライブラリをインストールした。
  • ディレクトリを作るサンプルプログラムで動作を確認した。
  • マニュアルの場所と基本的な関数,使用手順についてまとめた。

Posix90ライブラリに関する情報はほとんどなかったので,参考になれば嬉しい。

なお,このライブラリはUbuntuでしかビルドできなかった。けっこうな時間をかけてCygwinやMSYS2でのビルドも試してみたが,以下のようなエラーが出てしまい,どうにもできなかった。

cc -g -fno-leading-underscore   -c -o f90_unix_signal_const.o f90_unix_signal_const.c^M
f90_unix_signal_const.c: In function ‘main’:
f90_unix_signal_const.c:43:51: error: ‘SIGIOT’ undeclared (first use in this nction)
cc -g -fno-leading-underscore   -c -o f90_unix_env_const.o f90_unix_env_const.c
f90_unix_env_const.c: In function 'main':
f90_u
     nix_env_const.c:33:60: error: 'gid_t' undeclared (first use in this function)
     printf("integer, parameter :: GID_KIND = %d\n", sizeof(gid_t));

とりあえず,Linux環境では動作すると思っておくとよいだろう。

Management of empty directory for Git

Gitで空ディレクトリを管理する方法をまとめる。

プロジェクトのディレクトリ構造を維持したり,一時ファイルの管理のために空ディレクトリをGitでも管理したいことがある。しかし,Gitではファイルのみが管理対象となるため空ディレクトリを管理できない。そのため,空ディレクトリの管理には何らかのファイルを配置して行う。

配置するファイル内容により,空ディレクトリの管理方法には以下の2通りがある。

  1. 空ファイルの追加
  2. .gitignoreで指定

空ファイルの追加

管理したい空ディレクトリに空ファイルを追加して,トップディレクトリの.gitignoreでからファイル以外を無視する。空ファイルの名前はなんでもよく,とくに決まっていない。慣例として以下のどれかが使われているようだ。

  • .gitkeep
  • .gitignore
  • empty

.gitkeepというファイル名がRails関係などでよく使われているらしい。.gitignoreだと本来の.gitignoreと役割を勘違いする可能性があるらしい。emptyだとわかりやすい。

例:

mkdir tmp
touch tmp/empty
git add tmp/empty
利点
  • 無視ディレクトリだとわかりやすい。
欠点
  • 空ディレクトリにファイルが追加される場合,上位の.gitignoreで追加されたファイルを無視する必要がある。

例えば,以下のような.gitignoreを配置する必要がある。

tmp/*
!empty

.gitignoreで指定

空ディレクトリに以下の内容の.gitignoreを追加する。

*
!.gitignore
利点
  • 上位の.gitignoreを気にしなくてよく,シンプル。
欠点
  • 無視ディレクトリだと気づかれにくい。

まとめ

空ディレクトリの用途に合わせて,以下のように使い分けるといいようだ。

  • 空ファイルの追加:ソースコードなど,後から管理するファイルを追加する可能性があるとき
  • .gitignoreで指定:管理したくないキャッシュやログなどの一時ファイルを保持するとき

この記事は以下の記事の内容を自分用に整理したものとなっている。

参考:

Gitで空フォルダを管理したいときemptyを使うか.gitignoreを使うか - なんたらノート第三期ベータ http://tanakahisateru.hatenablog.jp/entry/20120120/1327044729

2014-12-25

Use of alias for Vim external command

Vimから外部コマンドの実行時にaliasを使う方法を記す。

Introduction

Vimを使っていると外部コマンドでaliasを使いたいときがある。たとえば,私は以下のように普段頻繁に使うlsコマンドをaliasで再定義したり,OSごとの異なるクリップボードへの書き出しコマンドをaliasでまとめることで,同じコマンドからアクセスできるようにしている。

alias ls="ls -AFh --color=auto"
#### Copy to clipboard
# [ `command -v clip` ]    && alias clip="clip" # MSYS (Windows)
[ "`command -v xsel`" ]    && alias clip="xsel -ib" # Linux
[ "`command -v putclip`" ] && alias clip="putclip" # Cygwin
[ "`command -v pbcopy`" ]  && alias clip="pbcopy" # Mac

これらのaliasをVimからも使えるようにしたい。Vimから外部コマンドを使うには,コマンドラインモードで!<command>という書式で実行する。

:!ls

ここでの外部コマンドでaliasを使えるようにしたい。

Method

私はログインシェルをbashにしており,そこからtmuxの起動と同時にzshにシェルが変わるようにしている。したがって,Vimでの外部コマンドは,/bin/bashから実行される。そのため,bashのシェルスクリプトで読み込む設定ファイルを読み込むようにしたらよい。

BASH_ENV環境変数は,bashにおいて非対話的に実行したときに読み込む設定ファイル(既定はなし)を指定できる(zshでは~/.zshenvのファイルが非対話実行時に読み込まれる)。したがって,aliasを書いた設定ファイルをこの変数に指定すればよい。ただし,このままだとVimから外部コマンドの実行時にaliasが展開されないためうまくいかない。そこで,bashのexpand_aliasesオプションを有効にすることでaliasを展開することができ,Vimから外部コマンドをaliasで利用できるようになる。

たとえば,冒頭に示したaliasの設定とaliasを展開するオプション設定を~/.bashenvに記述し,BASH_ENV環境変数の設定を~/.bashrcに書いておけばよいだろう。

## .bashenv
shopt -s expand_aliases
alias ls="ls -AFh --color=auto" #### Copy to clipboard # [ `command -v clip` ] && alias clip="clip" # MSYS (Windows) [ "`command -v xsel`" ] && alias clip="xsel -ib" # Linux [ "`command -v putclip`" ] && alias clip="putclip" # Cygwin [ "`command -v pbcopy`" ] && alias clip="pbcopy" # Mac

## .bashrc
export BASH_ENV="~/.bashenv"

ちなみにこのexpand_aliasesはaliasの設定前後のどちらに書いても問題なく動作する。

BASH_ENV環境変数に設定したファイルは非対話実行時に常に読み込まれる。そのため,シェルスクリプトなどで意図しない動作を起こす可能性があり危険である。そこで,Vimの起動時にだけBASH_ENV変数を設定し,Vimの実行中だけBASH_ENV変数を読みこめばいい。つまり,以下の内容を~/.vimrcに記述して~/.bashenvファイルが読み込み可能で存在するときにBASH_ENV変数を設定すればいい。

"" 外部コマンドでaliasを使えるようにする
if filereadable(glob('~/.bashenv'))
  let $BASH_ENV=expand('~/.bashenv')
endif

これに合わせて,~/.bashrcの設定からはBASH_ENVの設定を除去し,aliasの展開オプションだけを記述する。

shopt -s expand_aliases 

これによりVimから外部コマンド実行時にaliasを使えるようになった。

Appendix

なお,蛇足ではあるが自分の備忘録のために私の設定についても述べておく。

bashとzshは設定が似通っている部分があり,aliasやPATHなどの共通設定を~/.zbashrcファイルで管理し,~/.bashrc~/.zshrcの最後に以下を記述して,~/.zbashrcを読み込むようにしている。

## for shared shell setting
[ -e ~/.zbashrc ] && source ~/.zbashrc

~/.vimrcでのBASH_ENV環境変数の設定では~/.bashenvでなく~/.zbashrcを指定し,~/.zbashrcでは以下のようにシェルがbashのときだけexpand_aliasesを設定している。

## enable alias in non-interactive shell
shell=`readlink /proc/$$/exe`
case $shell in
  *bash) shopt -s expand_aliases;;
  # *zsh) setopt aliases;;
esac

[ "`command -v xsel`" ] &&  shell=`readlink /proc/$$/exe`は説明が必要と思われるが,今回はテーマが異なるため省略する。

参考:

  • Vimの外部コマンド実行でaliasを使う http://sanrinsha.lolipop.jp/blog/2013/09/vim-alias.html
  • $BASH_ENV - メモ帳 http://d.hatena.ne.jp/parasporospa/20061103/p2

2014-12-23

Solution of grep: warning: GREP_OPTIONS is deprecated; please use an alias or script

MSYS2のbashを起動すると以下の警告が表示されるようになった。

grep: warning: GREP_OPTIONS is deprecated; please use an alias or script

.bashrcで指定しているGREP_OPTIONが原因のようだ。

GREP_OPTIONS="--color=auto --binary-files=without-match"

この問題については以下での議論が参考になった。

Change to alias and rmove decaprated GREP_COLOR by mimi1vx · Pull Request #3341 · robbyrussell/oh-my-zsh https://github.com/robbyrussell/oh-my-zsh/pull/3341

この警告はgrep version 2.21から出るようになったようだ。grep 2.21のマニュアルのGREP_OPTIONSに以下の記載がある。

This variable specifies default options to be placed in front of any explicit options. As this causes problems when writing portable scripts, this feature will be removed in a future release of grep, and grep warns if it is used. Please use an alias or script instead.

スクリプトでこの変数が問題を起こすので、今後のリリースではこの変数をサポートしないらしい。引き続き設定を使うなら,警告にあるようにaliasにしたらいい。aliasはシェルスクリプトでは無視されるので安心だ。たとえば以下のように設定すれば問題ないだろう。

GREP_OPTIONS="--color=auto --binary-files=without-match" ## grep 2.21
alias grep="grep $GREP_OPTIONS"

これでターミナルの起動時に警告が表示されなくなり安心した。

2014-12-12

Add new account to Thunderbird

仕事用と個人用のメールを使い分けているが,Thunderbirdでローカル環境で一括で管理したい。そのため,Thunderbirdで新しくアカウントを追加する方法をまとめた。これによりネット上とローカル上でメールを保存でき,どちらかで問題があってもメールを確認できる。

ツール▷アカウント設定

アカウント操作▷メールアカウントを追加

名前とメールアドレス、パスワードを入力▷続ける

IMAPかPOP3を選択する。IMAPを選択して、リモートサーバーのメールを参照しつつ、ローカルにも保存する。

これでアカウントが登録される。

新しく登録したアドレス▷動機とディスク領域▷メッセージの同期▷以下にチェック▷OK

☑ このアカウントのメッセージをこのコンピュータに保存する

これでIMAPでローカルにもメールが保存されるようになった。