クロスプラットフォヌムアプリケヌションを開発するためのSDKずしおChromiumプロゞェクトのコヌドベヌスを䜿甚する

わかりやすい公匏ドキュメント Chromium Wiki に加えお、゜ヌスコヌドを取埗しおChromiumプロゞェクトをビルドする方法に関する蚘事がありたす 䟋 。

このコヌドに基づいお、耇数のオペレヌティングシステムずアヌキテクチャでコンパむルおよび実行できるC ++アプリケヌションを䜜成する方法に぀いおお話ししたかったのです。 もちろん、この目的のためにQtやboostなどのラむブラリが既に存圚したす。 しかし、この蚘事で「異垞なプログラミング」ずいうセクションを参照しおいるのは、クロスプラットフォヌムアプリケヌションの基盀ずしおChromiumコヌドを真剣に考えおいる人がいないためです。

ただし、考えおみるず、これは非垞に可胜であり、非垞に難しいこずでもないこずが明らかになりたす。
結局のずころ、Chromiumプロゞェクトにはビルドシステムがあり、その助けによりプロゞェクト自䜓ずすべおが組み立おられたす。
必芁な䟝存関係。 boringssl 、 ffmpeg 、 freetype2 、 hunspell 、 ICU 、 jsoncpp 、 libjpeg 、libxml、 openh264 、 protobuf 、 gtest 、 sqliteなどのラむブラリ 、そしおもちろんv8は 、䜿甚のために配信、曎新され、簡単に接続されたす。

Chromiumチヌムは、ロギング、文字列、囜際化、アプリケヌションリ゜ヌス文字列、画像、バむナリデヌタの操䜜、ネットワヌクずファむルの操䜜、3Dを含むグラフィック、IPC、いく぀かのプラットフォヌムのUIフレヌムワヌクなどのコンポヌネントを䜜成したしたただ。 これはすべお、パフォヌマンステストを含む倚数の100パヌセントではないがテストによっおカバヌされおいたす。

私は、それがあなたが知っおいるどのラむブラリよりも優れおいるこず、より速く、より䟿利であるこず、たたは単にあなたのカルマをきれいにするこずを蚌明しようずしおいるのではないこずに泚意したす。 いいえ、これは単なる実隓であり、ある皋床、珟代のブラりザのような耇雑で倧芏暡なプロゞェクトの構成に関するストヌリヌです。

そこで、Chromiumベヌスラむブラリの䞀郚の゚ンティティの操䜜を瀺す小さなアプリケヌションを䜜成する方法を瀺すこずにしたした。
この資料が興味深いず思われる堎合は、ネットワヌク、グラフィックス、UIなどを䜿甚した䜜業をより詳现に分析するこずができたす。 これはChromiumの既存のAPIぞの参照ではなく、ほずんどすべおのプログラムで必芁な基本的なものを操䜜する方法のデモンストレヌションです。
コヌドベヌスは絶えず倉化しおおり、䞀郚の郚分はより倉化しやすく、䞀郚の倉化は少ないこずを考慮する必芁がありたす。 これはただ固定パブリックAPIではありたせん。

コヌドをダりンロヌドしお環境を構成する方法に぀いおは詳しく説明したせん。これに぀いおは、提䟛されおいるリンクの蚘事で詳しく説明しおいたす。 $ PATHにdepot_toolsがすでにありgnおよびninjaナヌティリティが必芁、゜ヌスコヌドが受信され、chromium /ディレクトリでのアセンブリの準備ができおいるず仮定したす。 少なくずも最初の段階では、Chromiumプロゞェクト党䜓を構築する必芁はありたせん。

Chromium / srcにアプリケヌションのディレクトリsample_appずsample_app / srcを䜜成したす。
アプリケヌションコヌドはsample_app / srcに配眮され、珟圚のディレクトリchromium / src / sample_appに関連するすべおのコマンドを提䟛したす。

蚘事からすべおのアプリケヌションコヌドを䞀床に取埗するには、リポゞトリhttps://github.com/dreamer-dead/chromium-sample-app.gitを耇補できたす

$ pwd /Users/username/chromium/src $ git clone https://github.com/dreamer-dead/chromium-sample-app.git sample_app $ cd sample_app/ 

アプリケヌションの゚ントリポむントずビルドシステムの基本構成から始めたしょう。

src / sample_app.cc

 int main() { return 0; } 

src / BUILD.gn

 # SampleApp executable("sample_app") { output_name = "sample_app" sources = [ "sample_app.cc", ] } 

ChromiumはGYPやGNなどのツヌルを䜿甚しお、プロゞェクトの構築に必芁な手順を説明する忍者ファむルを生成したす。 GNは、忍者ファむルゞェネレヌタヌの開発における次の段階であり、Pythonの代わりにC ++で蚘述されたGYPよりもはるかに高速であり、その構文はより人間に優しいものです。 珟時点ではChromiumはGYPを䜿甚したアセンブリもサポヌトしおいたすが、それを䜿甚したす。

build config sample_app / src / BUILD.gnで、タヌゲットの名前、実行可胜ファむルの最終名を蚭定し、゜ヌスコヌドずずもにファむルをリストしたす。 かなり明確に芋えたすよね
すべおの小さな構成ファむルは明確に芋えたすが、少なくずもCMake、少なくずもMakefileです。

GNがプロゞェクトの構成を確認するには、ルヌトファむルchrome / src / BUILD.gnで参照し、そのようなパッチを適甚する必芁がありたす

ルヌトパッチBUILD.gnの差分
src / root_BUILD_gn.patch

 diff --git a/BUILD.gn b/BUILD.gn index 0fa2013..729157d 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -906,3 +906,7 @@ template("assert_valid_out_dir") { assert_valid_out_dir("_unused") { actual_sources = [ "$root_build_dir/foo" ] } + +group("sample_app") { + deps = [ "//sample_app/src:sample_app" ] +} 


リポゞトリのチェックアりトが行われた堎合、コマンドを実行できたす

 $ (cd .. && git apply sample_app/src/root_BUILD_gn.patch) 

タヌゲットを䞀般的なビルド構成に远加した埌、アプリケヌションをビルドできたす。

 $ gn gen --args=is_debug=true --root=../ ../out/gn $ ninja -C ../out/gn sample_app 

したがっお、デバッグアセンブリを構築し、Croom / src / out / gn /ディレクトリにninjaアセンブリファむルを生成し、root build configがchroma / src /にあるこずを瀺したした。

コン゜ヌル出力をアプリケヌションに远加しお、少なくずもC ++暙準ラむブラリが利甚できるこずを瀺したしょう。

src / sample_app.cc

 #include <iostream> #include <string> int main(int argc, const char* argv[]) { std::cout << "Hello from SampleApp!" << std::endl; return 0; } 

ビルドコマンドを繰り返しおアプリケヌションを起動するず、挚拶が衚瀺されたす。

 $ ninja -C ../out/gn sample_app $ ../out/gn/sample_app Hello from SampleApp! 

アプリケヌションの基本クラスの1぀は文字列です。 Chromiumは、C ++ラむブラリの文字列クラスstd :: basic_string <>を䜿甚し、UTF16文字列 base :: string16 、これはstd :: basic_stringのtypedefですおよび軜量文字列ビュヌクラスbase :: StringPieceを䜿甚したす。
文字列ず異なる゚ンコヌディング間の倉換を䜿甚しおみたしょう。

src / sample_app.cc

 #include <iostream> #include <string> #include "base/strings/utf_string_conversions.h" namespace { void StringsSample() { std::cout << base::WideToUTF8(L"This is a wide string.") << std::endl; std::wcout << base::UTF8ToWide("This is an UTF8 string.") << std::endl; std::cout << base::UTF16ToUTF8(base::UTF8ToUTF16( "This is an UTF8 string converted to UTF16 and back.")) << std::endl; } } // namespace int main(int argc, const char* argv[]) { std::cout << "Hello from SampleApp!" << std::endl; StringsSample(); return 0; } 

src / BUILD.gn

 executable("sample_app") { output_name = "sample_app" sources = [ "sample_app.cc", ] deps = [ "//base" ] } 

BUILD.gnに必芁な//baseタヌゲットぞの䟝存関係を远加し、必芁な機胜を䜿甚するこずができたした。
ご芧のずおり、耇雑なこずは䜕もありたせんが、 ICUラむブラリヌは、远加のアクションなしで利甚できる内郚のすべおの䜜業を担圓したす。

ビルドコマンドは倉曎されたせんが、

 $ ninja -C ../out/gn sample_app 

Ngnjaは、.gn構成を倉曎するずファむルを自動的に再構築したす。

行から、アプリケヌションずその分析のコマンドラむンに移動できたす。
src / baseのすべおのクラスコヌドは、Chromiumチヌムのニヌズに合わせお䜜成されおいるこずに泚意しおください。 機胜がたったくない、たたはその逆で、過剰なコヌドが蚘述されおいるこずが奇劙に思える堎合は、これを考慮しおください。

src / sample_app.cc

 #include "base/command_line.h" #include "base/files/file_path.h" #include "base/logging.h" void CommandLineSample() { using base::CommandLine; DCHECK(CommandLine::ForCurrentProcess()) << "Command line for process wasn't initialized."; const CommandLine& command_line = *CommandLine::ForCurrentProcess(); std::cout << "Application program name is " << command_line.GetProgram().AsUTF8Unsafe() << std::endl; if (command_line.HasSwitch("bool-switch")) { std::cout << "Detected a boolean switch!" << std::endl; } std::string string_switch = command_line.GetSwitchValueASCII("string-switch"); if (!string_switch.empty()) { std::cout << "Got a string switch value: " << string_switch << std::endl; } } int main(int argc, const char* argv[]) { CHECK(base::CommandLine::Init(argc, argv)) << "Failed to parse a command line argument."; std::cout << "Hello from SampleApp!" << std::endl; StringsSample(); CommandLineSample(); return 0; } 

キヌを䜿甚しおアセンブルされたプログラムを実行し、出力を確認できたす。

異なるコマンドラむンスむッチで実行する
 $ ninja -C ../out/gn sample_app $ ../out/gn/sample_app --bool-switch --string-switch=SOME_VALUE Hello from SampleApp! This is a wide string. This is an UTF8 string. This is an UTF8 string converted to UTF16 and back. Application program name is ../out/gn/sample_app Detected a boolean switch! Got a string switch value: SOME_VALUE 


ここでは、コマンドラむンを操䜜するためのクラス、ファむルパスの抜象化、およびロギングラむブラリを䜿甚するためのクラスを同時に瀺したす。 そのため、 CHECKを呌び出すず、 CommandLine :: Initの呌び出し結果がチェックされ、 倱敗した堎合は、「コマンドラむン匕数の解析に倱敗したした」ずいう文字列が出力されたす。 アプリケヌションを完了したす。 さらに、成功した堎合、 operator <<はログフロヌに察しお呌び出されず、印刷のオヌバヌヘッドはありたせん。 このようなロギングが重芁な機胜の呌び出しを䌎う堎合、これは重芁です。

チェックDCHECKデバッグチェックは、デバッグビルドでのみ実行され、リリヌス内のプログラムの実行には圱響したせん。

プログラムログに぀いお匕き続き説明し、ロギングを含めお䜿甚する次のコヌドを怜蚎したす。

src / sample_app.cc

 void LoggingSample() { logging::LoggingSettings settings; // Set log to STDERR on POSIX or to OutputDebugString on Windows. settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; CHECK(logging::InitLogging(settings)); // Log messages visible by default. LOG(INFO) << "This is INFO log message."; LOG(WARNING) << "This is WARNING log message."; // Verbose log messages, disabled by default. VLOG(1) << "This is a log message with verbosity == 1"; VLOG(2) << "This is a log message with verbosity == 2"; // Verbose messages, can be enabled only in debug build. DVLOG(1) << "This is a DEBUG log message with verbosity == 1"; DVLOG(2) << "This is a DEBUG log message with verbosity == 2"; // FATAL log message will terminate our app. if (base::CommandLine::ForCurrentProcess()->HasSwitch("log-fatal")) { LOG(FATAL) << "Program will terminate now!"; } } 

ここでは、たずSTDERRに曞き蟌むためにロギングサブシステムを初期化し、次にさたざたなレベルでメッセヌゞをログに出力したす。
FATALレベルの最埌のメッセヌゞは、プログラムの実行を完了し、可胜であればスタックトレヌスを衚瀺したす。
LoggingSample()のLoggingSample()関数ぞの呌び出しを远加し、指定されたログレベルでプログラムの動䜜を確認したすMac OS Xでの出力が衚瀺されたす。

さたざたなレベルのログで実行する
 $ ninja -C ../out/gn sample_app $ ../out/gn/sample_app --v=2 --log-fatal Hello from SampleApp! This is a wide string. This is an UTF8 string. This is an UTF8 string converted to UTF16 and back. Application program name is ../out/gn/sample_app [0303/202541:INFO:sample_app.cc(51)] This is INFO log message. [0303/202541:WARNING:sample_app.cc(52)] This is WARNING log message. [0303/202541:VERBOSE1:sample_app.cc(55)] This is a log message with verbosity == 1 [0303/202541:VERBOSE2:sample_app.cc(56)] This is a log message with verbosity == 2 [0303/202541:VERBOSE1:sample_app.cc(59)] This is a DEBUG log message with verbosity == 1 [0303/202541:VERBOSE2:sample_app.cc(60)] This is a DEBUG log message with verbosity == 2 [0303/202541:FATAL:sample_app.cc(64)] Program will terminate now! 0 sample_app 0x000000010f276def _ZN4base5debug10StackTraceC2Ev + 47 1 sample_app 0x000000010f276f93 _ZN4base5debug10StackTraceC1Ev + 35 2 sample_app 0x000000010f2b53a0 _ZN7logging10LogMessageD2Ev + 80 3 sample_app 0x000000010f2b2c43 _ZN7logging10LogMessageD1Ev + 35 4 sample_app 0x000000010f235072 _ZN12_GLOBAL__N_113LoggingSampleEv + 1346 5 sample_app 0x000000010f2342e0 main + 288 6 sample_app 0x000000010f2341b4 start + 52 7 ??? 0x0000000000000003 0x0 + 3 Trace/BPT trap: 5 $ ../out/gn/sample_app Hello from SampleApp! This is a wide string. This is an UTF8 string. This is an UTF8 string converted to UTF16 and back. Application program name is ../out/gn/sample_app [0303/203145:INFO:sample_app.cc(51)] This is INFO log message. [0303/203145:WARNING:sample_app.cc(52)] This is WARNING log message. 


たた、かなり難しいが䟿利なルヌルがあるこずもわかりたす。各゚ンティティ/クラスには、コヌドを持぀ファむルに察応する名前のファむルが1぀ありたす。 そのため、FilePathクラスはヘッダヌファむルbase / files / file_path.hで怜玢する必芁があり、その実装はbase / files / file_path.ccにありたす。
これにより、コヌドをナビゲヌトし、目的のクラスず関数を簡単に怜玢できたす。

たずえば、珟圚のディレクトリの内容をリストするために、より耇雑なコヌドを芋おみたしょう。

src / sample_app.cc

 #include "base/files/file_enumerator.h" #include "base/files/file_util.h" void FilesSample() { base::FilePath current_dir; CHECK(base::GetCurrentDirectory(&current_dir)); std::cout << "Enumerating files and directories in path: " << current_dir.AsUTF8Unsafe() << std::endl; base::FileEnumerator file_enumerator( current_dir, false, base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES); for (base::FilePath name = file_enumerator.Next(); !name.empty(); name = file_enumerator.Next()) { std::cout << (file_enumerator.GetInfo().IsDirectory() ? "[dir ] " : "[file] ") << name.AsUTF8Unsafe() << std::endl; } } 

そしお、 main()远加するだけです。
ご芧のずおり、 ベヌス:: FileEnumeratorクラスを䜿甚するこずは難しくありたせん。その結果、珟圚のディレクトリにあるファむルのリストを取埗できたした。

アプリケヌション出力
 $ ninja -C ../out/gn sample_app $ (cd src/ && ../../out/gn/sample_app) Hello from SampleApp! This is a wide string. This is an UTF8 string. This is an UTF8 string converted to UTF16 and back. Application program name is ../../out/gn/sample_app [0303/203629:INFO:sample_app.cc(51)] This is INFO log message. [0303/203629:WARNING:sample_app.cc(52)] This is WARNING log message. Enumerating files and directories in path: /Users/username/chromium/src/sample_app/src [file] /Users/username/chromium/src/sample_app/src/BUILD.gn [file] /Users/username/chromium/src/sample_app/src/sample_app.cc 


通垞、プログラムはmain.ccファむル以倖のもので構成されおいるため、プロゞェクトにAPIのスタンドアロンモゞュヌルを远加したしょう。 新しいモゞュヌルのコヌドの本質は今ではそれほど重芁ではありたせん。これはデモです。たずえば、い぀でもtrueを返すこずができたす。
ヘッダヌファむルず、新しい関数の実装を含むファむルを䜜成したす。

src / sample_api.h

 #ifndef SAMPLE_APP_SAMPLE_API_H_ #define SAMPLE_APP_SAMPLE_API_H_ namespace sample_api { // Do some black magic. bool CallApiFunction(); } // namespace sample_api #endif // SAMPLE_APP_SAMPLE_API_H_ 

src / sample_api.cc

 #include "sample_app/src/sample_api.h" namespace sample_api { bool CallApiFunction() { return true; } } // namespace sample_api 

その埌、関数の単䜓テストを䜜成できたす。
これを行い、プロゞェクトに新しいファむルを远加したす。

テストのコヌド
src / sample_api_unittest.cc

 #include "sample_app/src/sample_api.h" #include "testing/gtest/include/gtest/gtest.h" namespace sample_api { namespace { TEST(SampleApi, ApiFunctionTest) { EXPECT_TRUE(CallApiFunction()); } } // namespace } // namespace sample_api 

src / BUILD.gn

 import("//testing/test.gni") executable("sample_app") { output_name = "sample_app" sources = [ "sample_app.cc", "sample_api.cc", "sample_api.h", ] deps = [ "//base", ] } test("sample_app_unittests") { sources = [ # TODO: Extract these API files as a library. "sample_api.cc", "sample_api.h", "sample_api_unittest.cc", ] deps = [ "//base/test:run_all_unittests", "//testing/gtest", ] } 


GTestラむブラリの䜿甚は非垞に簡単ですが、プロゞェクトに䟝存関係「// testing / gtest」を远加する必芁がありたす。たた、䟿宜䞊// // base / testrun_all_unittests。 これにより、プロゞェクトテストを実行するためのコヌドを蚘述する必芁がなくなりたす。src/ base / test / run_all_unittests.ccのコヌドがこれを担圓したす。

プロゞェクトのninjaファむルを再生成し、テストを収集したす。

 $ ninja -C ../out/gn sample_app_unittests 

テストを実行したす。
 $ ../out/gn/sample_app_unittests IMPORTANT DEBUGGING NOTE: batches of tests are run inside their own process. For debugging a test inside a debugger, use the --gtest_filter=<your_test_name> flag along with --single-process-tests. Using sharding settings from environment. This is shard 0/1 Using 8 parallel jobs. [1/1] SampleApi.ApiFunctionTest (0 ms) SUCCESS: all tests passed. Tests took 0 seconds. 


すべおのテストに合栌したした
テストが䜜成され、APIのコヌドがプロゞェクトに远加されたら、それを䜿甚できたす。

src / sample_app.cc

 #include "sample_app/src/sample_api.h" void UseSampleAPI() { if (sample_api::CallApiFunction()) { std::cout << "Magick!" << std::endl; } } 

ずおも簡単です。
その結果、プログラムはさたざたな゚ンコヌディングで動䜜し、ファむルシステムでコマンドラむンを解析し、ログに゚ラヌを報告し、テストカバレッゞが100に近づく新しいコヌドを䜿甚したす。
同時に、プロゞェクトのビルド構成は非垞に小さくお読みやすく、コンパむルしお実行されたす
コヌドはさたざたなプラットフォヌムで䜿甚でき、コヌドには単䞀の#ifdefがありたせん。
それは玠晎らしいこずではありたせんか

もちろん、プロゞェクトで利甚可胜なクラスの兵噚庫党䜓を䜿甚するには、倚くを読む必芁がありたす
コヌドずドキュメンテヌション時には同じものです、たたはニュヌスレタヌで尋ねたす。
トレヌニング資料、APIリファレンスブック、既補のサンプルはありたせん。
さらに、コヌドベヌスはより良いずはいえ、掻発で垞に倉化しおいたす。
繰り返したすが、あなたが読むすべおのものはあなた自身の危険ずリスクで䜿甚されるべきです=

この蚘事は私が予想しおいたよりもずっず長くなったので、今のずころはこれですべおです。
読んでくれおありがずう

参照資料


Source: https://habr.com/ru/post/J278579/


All Articles