移植が簡単なGoコードの書き方

(Goで真のクロスプラットフォームコードを記述するためのヒントを含む記事の翻訳)

Goは、さまざまなプラットフォームでの作業に最適です。 私の主な開発環境はWindowsですが、常にLinuxシステムを使用しています。 したがって、私は自然に問題を引き起こす可能性のあるものを避けようとします。







クロスプラットフォーム開発に対する私の姿勢は、自分を真面目な開発者と見なす場合は、少なくとも他のプラットフォームでコードをビルドする必要があるということです。他のプラットフォーム上のライブラリ。



最近、ジャーナリングと圧縮に重点を置いた非常に優れたアーカイバであるzpaqの代替案を検討したかったため、Windowsバージョンの非常に優れたバックアッププログラムの作成を支援しました。 移植中、私は他の人にとって役に立つかもしれないいくつかのことに注意しました。



syscall(またはsys)の使用を最小限に抑える



明白なことから始めましょう。 syscallパッケージはプラットフォームごとに異なりますが、共通点はありますが、問題が発生することはほぼ保証されています。 もちろん、それを使用する非常に良い理由がありますが、それを使用する前に、同じことをする他の方法がないことを確認してください。 syscall / sysを使用する場合は、すぐに、移植に必要なアセンブリタグを使用して個別のファイルを作成する必要があることを準備します。たとえば、// + darwin dragonfly freebsd linux netbsd openbsd solarisをビルドし、他のプラットフォーム。



github.com/golang/sysパッケージもあります。これは、システムコールを各プラットフォームの個別のパッケージに配布します。 ただし、これは問題を解決しないため、ここでも同じ考慮事項が適用されます。



信号に依存しないようにしてください





signalάνοςbyσαλιγόπουλοςによる「信号なし」



シグナルは非常に便利です。 SIGHUPを使用して構成を再ロードするサーバー、SIGUSR2を使用してサービスを再起動するサーバーなどが多数あります。 これらの信号は他のプラットフォームでは利用できないため、 主な機能をそれらに依存させないでください。 上記の例のないWebサーバーは、一部の機能がなくてもWindowsで正常に機能します。 もちろん、Windowsに対して同様のソリューションを用意する方が良いでしょうが、サーバーがコンパイルされて正常に動作する限り、誰も気にしないと思います。



したがって、たとえば、特定の機能するサービスを作成する場合、サービスにコマンドを送信する唯一の方法はシグナルにしないでください。



ファイルシステムの違い



ファイルシステムは異なることに注意してください。



ところで、最後のポイントは、私が出会った最も一般的な間違いです。

func example() (err error) { var f *os.File f, err = os.Create("myfile.txt") if err != nil { return } defer f.Close() err = f.write([]byte{"somedata"}) if err != nil { return } // Do more work... err = os.Remove("myfile.txt") }
      
      





これはそれほど明白なエラーではありませんが、簡単な例があるため、ファイルを閉じるに削除しようとしていることがわかります。



問題は、これがほとんどのシステムでうまく機能することですが、Windowsでは落ちます。 これを行うためのより正しい例は次のとおりです。

 func example() (err error) { var f *os.File f, err = os.Create("myfile.txt") if err != nil { return } defer func() { f.Close() err2 := os.Remove("myfile.txt") if err == nil && err2 != nil { err = err2 } }() err = f.write([]byte{"somedata"}) // Do more work }
      
      





ご覧のとおり、ファイルを閉じる順序を維持することは簡単な作業ではありません。 2つの遅延を使用するアプローチを選択する場合、遅延の呼び出しは逆の順序で実行されるため、閉じる前にos.Removeを定義する必要があることに注意してください。



WindowsとLinuxのファイルシステムの違いを説明するより詳細な記事があります。



ANSIを使用する場合はトランスレーターを使用します





コンソールコマンドを使用して出力をフォーマットすることは、良い解決策です。 これにより、テキストをスクロールすることなく、色を使用したり進行状況を表示したりすることで、視覚的な認識を容易にすることができます。



ANSIコードを使用する場合は、常にトランスレーターライブラリを使用する必要があります。 すぐに使用して、後で頭痛から自分を救います。 ランダムに見つけたものをいくつか紹介します。



他の良いライブラリを知っているなら、コメントに書いてください。



シンボリックリンクへの依存を回避する



シンボリックリンクは素晴らしいことです。 これにより、新しいバージョンのファイルを作成したり、ファイルの最新バージョンを自動的に指すリンクを作成したりするなど、クールなことができます。 ただし、Windowsでは、プログラムに管理者権限がある場合にのみシンボリックリンクを作成できます。 したがって、これはちょっとしたことですが、プログラムの機能がそれに依存しないことを確認してください。



可能であれば、CGOおよび外部プログラムを避けてください。



Windowsでビルドするための作業環境をセットアップするのは非常に難しいため、できるだけ早くCGOを避けるように努力する必要があります。 cgoを使用すると、WindowsだけでなくAppEngineユーザーも拒否します。 コードがライブラリではなくプログラムである場合は、Windowsでもバイナリファイルをアップロードする準備をしてください。



同じことが外部プログラムにも当てはまります。 ライブラリで解決できるタスクの使用を最小限に抑え、外部プログラムは複雑なタスクにのみ使用してください。



コンパイル時または実行時?



多くの場合、ある種のOSで作業するとき、OS固有のコードをどのように記述するか疑問に思います。 ある点を満たすために必要なのはプラットフォーム固有のコードかもしれません。 例を挙げましょう。 次の機能。 多くのことを行いますが、要件の1つは、Windows以外のプラットフォームでは、ファイルを読み取り専用で作成する必要があることです。 2つの実装を見てみましょう。

 func example() { filename := "myfile.txt" fi, _ := os.Stat(f) // set file to readonly, except on Windows if runtime.GOOS != "windows" { os.Chmod(f, fi.Mode()&os.FileMode(^uint32(0222))) } }
      
      





これにより、実行時にプログラムがWindows上で実行されているかどうかがチェックされます。



これと比較してください:

 func example() { filename := "myfile.txt" fi, _ := os.Stat(f) setNewFileMode(f, fi) } // example_unix.go //+build !windows // set file to readonly func setNewFileMode(f string, fi os.FileInfo) error { return os.Chmod(f, fi.Mode()&os.FileMode(^uint32(0222))) } // example_windows.go: // we don't set file to readonly on Windows func setNewFileMode(f string, fi os.FileInfo) error { return nil }
      
      





多くの人が言うように、最新バージョンはこれを行う必要があります。 これが常に最良の解決策とは限らないことを示すために、意図的に例を複雑にしました。 私にとっては、最初のオプションが好ましい場合があります。特にそのようなコードの場所が1つしかない場合-これが短い場合、コードが何をするかを確認するためにいくつかのファイルを調べる必要はありません。



私はそれぞれのアプローチのマイナスとプラスで小さなタブレットを作りました。

「コンパイル時」の長所 「実行時」の長所
最小限のオーバーヘッドまたは欠落したオーバーヘッド すべてのコードを1か所に保管できます
プラットフォームごとに個別のファイルのコード 一部のエラーは、クロスコンパイルせずに検出できます。
すべてのプラットフォームでコンパイルされないインポートを使用できます




「コンパイル時」の短所 「実行時」の短所
コードの複製が必要になる場合があります。 プラットフォーム固有のコードがどこにあるかを確認する簡単な方法はありません
多くの小さなファイルにつながる可能性があり、多くの機能が分散した1つの大きなファイルにつながる可能性があります チェックするためのわずかなオーバーヘッド
コードを表示するには、いくつかのファイルを開く必要があります プラットフォームに依存しない構造/機能は使用できません
クロスコンパイルを使用して、コードが実行されることを確認する必要があります


一般に、テストを作成する場合を除き、コンパイル時に異なるファイルでチェックすることをお勧めします。 ただし、場合によっては、実行時に確認することをお勧めします。



クロスプラットフォームテストのCI構成



結論として、クロスプラットフォームテストを構成します。 これはresticから学んだ便利なことの1つであり、クロスコンパイルはすでに構成されています。 Go 1.5がリリースされると、設定する身体の動きがさらに少なくなるため、クロスプラットフォームコンパイルがさらに簡単になります。



同時に、Goの古いバージョンについては、クロスコンパイルの自動化に役立つgoxを見ることができます。 さらに高度な機能が必要な場合は、 goxcをご覧ください



ハッピーコーディング!



著者から:




All Articles