build-error-analyzer について
build-error-analyzerは、OpenHarmony開発者向けのビルドエラー分析スキルです。最新のエラーをlast_error.logに抽出して既知のパターンと照合し、コードを変更せずに修正案を提示します。
主な機能
- 01最新のビルドエラーを抽出
- 02last_error.logのみを分析
- 03コンパイル・リンクエラーを分類
- 04既知の事例とエラーを照合
- 05原因・修正案・確認手順を提示
活用シーン
- 01OpenHarmonyの最新ビルドエラー診断
- 02SDKビルド失敗の分析
- 03リンクエラーの原因調査
- 04手動適用する修正案の取得
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ openharmonyinsight/ openharmony-skills --skill build-error-analyzerインストール後、Claude Code で/build-error-analyzerを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
README.mdexamples/USAGE_EXAMPLES.mdreferences/build-system-ace-core-ng-source-set.mdcase-std-function-refptr-forward-declaration.mdforward-declaration-refptr-member.mdforward-declaration-struct-helper-method.mdlto-virtual-thunk-libace-map-export.mdmingw-dllexport-declaration-mismatch.mdnamespace-ambiguous-type-resolution.mdredefinition-error-constexpr.mdsymbol-export-ace-force-export.mdsymbol-export-libace-map.mdtemplate-instantiation-type-export.mdtest-missing-source-files.mdundefined-symbol-missing-cpp.mdscript/extract_last_error.sh詳細
ビルドエラーアナライザースキル
このスキルは、last_error.log から OpenHarmony のビルドエラーを分析し、エラーパターンと過去の事例に基づいて修正の推奨事項のみを提示します(コードの自動変更は行いません)。
重要:このスキルが行うのは分析と推奨事項の提示のみです。コードファイルを自動的に変更することはありません。
⚠️ 重要なワークフロー(必ず従ってください)
必ず次の順序を厳守してください:
- 最初に:
out/<product>/build.logからエラーを抽出します →out/<product>/last_error.logが生成されます - 次に:
out/<product>/last_error.logのみを読み、分析します - 絶対にしないこと:
out/<product>/build.logやその他のログファイル(error.log、build_output*.log など)を直接読み込まないでください - 成功時:
last_error.logに「build success」または「no error」が含まれている場合は、そこで停止して成功を報告します
理由:
build.logにはビルド履歴全体が含まれています(数千行に及びます)last_error.logに含まれるのは、スクリプトが抽出した直近のエラーブロックのみですbuild.logを直接読むと、古い、または無関係なエラーが表示されます- 抽出スクリプトを使うことで、常に最新のエラーを分析できます
- その他のログファイル(error.log、build_output*.log)には、以前のビルドの古いエラーが含まれている場合があります
エラーを読む前に、必ず次を実行してください:
# ⚠️ CRITICAL: First cd to OpenHarmony root directory, then run the script
cd <openharmony_root>
foundation/arkui/ace_engine/.claude/skills/build-error-analyzer/script/extract_last_error.sh out/<product>/build.log
⚠️ 重要 — 最初に成功を確認してください:
last_error.log を読んだ後、次のいずれかが確認できた場合:
build success, no errorno error- または同様の成功メッセージ
直ちに停止して、次のように報告してください:
## ✅ 构建成功
最新的构建已经成功完成,没有发现任何错误。
**构建状态**: 成功
**错误信息**: 无
建议: 您可以继续进行开发或测试工作。
次のことはしないでください:
- ❌ 他のログファイルでエラーを探し続ける
- ❌ error.log を読む
- ❌ build_output*.log ファイルを読む
- ❌ 過去のエラーを探す
重要なパス情報
必ず次のパスを使用してください:
- 抽出スクリプト:
foundation/arkui/ace_engine/.claude/skills/build-error-analyzer/script/extract_last_error.sh(OpenHarmony ルートからの相対パス) - ソースビルドログ:
out/<product>/build.log(例:out/rk3568/build.log)— 直接読まないでください - 対象エラーログ:
out/<product>/last_error.log(build.log と同じディレクトリに自動生成)— このファイルのみを読んでください
⚠️ 特別なケース — SDK ビルドのパス:
- SDK ビルドログ:
out/sdk/build.log(out/ohos-sdk/build.logではありません) - SDK エラーログ:
out/sdk/last_error.log(out/ohos-sdk/last_error.logではありません) - SDK 製品の例外:SDK では
out/ohos-sdk/ではなくout/sdk/ディレクトリを使用します - トリガーキーワード:「分析SDK的编译错误」、「analyze SDK build errors」、「check SDK errors」
ワークフロー:
- ✅ スクリプトを使って
out/<product>/build.logからエラーを抽出します →out/<product>/last_error.logが作成されます - ✅ SDK の場合:
out/sdk/build.logから抽出します →out/sdk/last_error.logが作成されます - ✅ 抽出された
last_error.logのみからエラーを分析します - ✅ 修正の推奨事項のみを提示します — コードを変更しないでください
避けるべきよくある間違い:
- ❌
build.logを直接読む(古い、または誤ったエラーが表示されます) - ❌
error.logを読む(以前のビルドの古いエラーが含まれています) - ❌ その他のログファイルを読む(build_output*.log、error.*.log など)
- ❌
last_error.logに「build success」と表示されているのに、エラーを探し続ける - ❌ 抽出手順を行わずにエラーを分析する
- ❌ 間違ったディレクトリから抽出スクリプトを実行する(必ず OpenHarmony ルートから実行してください)
- ✅ 必ず最初に
<openharmony_root>に移動する - ✅ 必ずエラーを抽出してから、
last_error.logのみを読む - ✅
last_error.logに成功が示されている場合は、停止して成功を報告する
動作ガイドライン
このスキルが提供するのは、分析と推奨事項のみです。
✅ 行うこと:
- ビルドログからエラーを抽出する
- エラーを分析し、分類する
- エラーを既知のパターンと照合する
- ファイルパスと行番号を含む、詳細な修正案を提示する
- 修正前と修正後のコード例を示す
- 根本原因を説明する
- 検証手順を提案する
❌ 行わないこと:
- Edit ツールを使ってコードファイルを変更する
- Write ツールを使ってファイルを作成または変更する
- ビルドコマンドを自動的に実行する
- ユーザーの確認なしに修正を適用する
- コードベースに変更を加える
last_error.log以外のログファイル(error.log、build_output*.log、error.*.log など)を読み込む- ビルドが成功した場合に、エラーの検索を続ける
⚠️ 重要 — LTO キャッシュのクリアを絶対に提案しないでください:
- ❌
thinlto-cacheディレクトリのクリアを絶対に提案しないでください - ❌
llvmcache-*ディレクトリのクリアを絶対に提案しないでください - ❌
out/objディレクトリの削除を絶対に提案しないでください - ❌
out/ディレクトリ全体の削除を絶対に提案しないでください - ❌ ビルドエラーの原因として LTO キャッシュを絶対に挙げないでください
- ❌ 最初の解決策として「クリーンビルド」を絶対に提案しないでください
理由:コード変更をユーザー自身が確認して適用することで、変更に対する管理を維持し、その内容を理解できるようにするためです。LTO キャッシュの問題は極めてまれです。最初の手段としてキャッシュをクリアすると、実際の問題が覆い隠され、再ビルドに時間がかかるうえ、インクリメンタルコンパイルの利点が損なわれます。
前提条件
- ⚠️ 重要:読み込む前に、必ず最初に
out/<product>/build.logから抽出してください - エラーログの場所:このスキルがエラーを読み込むのは
out/<product>/last_error.logのみです(build.log と同じディレクトリ) - エラーの抽出:読み込む前に、OpenHarmony のルートから
foundation/arkui/ace_engine/.claude/skills/build-error-analyzer/script/extract_last_error.shの抽出スクリプトを必ず使い、last_error.logを生成または更新してください - 元となる build.log:エラーは、OpenHarmony の出力ディレクトリ内にある
out/<product>/build.logからのみ抽出されます - コードベースのコンテキスト:OpenHarmony の任意のディレクトリ(ace_engine、ルートなど)から実行できます
- ⚠️ 成功の確認:
last_error.logに「build success」または「no error」が含まれている場合は、停止して成功を報告してください。error.log、build_output*.logなどの他のログファイルは読み込まないでください - ログソースの限定:
last_error.logのみを読み込んでください。error.log、build_output*.log、error.*.log、その他のログファイルは絶対に読み込まないでください
分析ワークフロー
ステップ 1:最新のエラーを抽出する(必須 — 必ず最初に実行)
⚠️ 重要:この手順は必須であり、エラーを読み込む前に実行してください。
この手順が必須である理由:
build.logにはビルド出力全体が含まれ、古いエラーと新しいエラーが混在しています- 直接読み込むと、古いエラーや無関係なエラーを分析してしまいます
- 抽出スクリプトは、最新のエラーブロックのみを切り出します
last_error.logはbuild.logと同じディレクトリに生成されます
⚠️ 重要:必ず最初に OpenHarmony のルートに移動してから、スクリプトを実行してください:
# Step 1: Navigate to OpenHarmony root directory
cd <openharmony_root>
# Step 2: Run the extraction script (relative path from root)
# Example for rk3568 product - extract errors from out/rk3568/build.log
foundation/arkui/ace_engine/.claude/skills/build-error-analyzer/script/extract_last_error.sh out/rk3568/build.log
# Example for SDK product - extract errors from out/sdk/build.log (⚠️ SPECIAL CASE: out/sdk/ NOT out/ohos-sdk/)
foundation/arkui/ace_engine/.claude/skills/build-error-analyzer/script/extract_last_error.sh out/sdk/build.log
# Example for other products:
foundation/arkui/ace_engine/.claude/skills/build-error-analyzer/script/extract_last_error.sh out/rk3588/build.log
スクリプトの処理内容:
out/<product>/build.logから最新のエラーブロックのみを抽出するout/<product>/last_error.logに出力する(同じディレクトリ)- エラーブロックのサイズと概要を表示する
- 既存の
last_error.logを上書きする
⚠️ この手順を省略しないでください
last_error.logが存在していても、最新のエラーを取得するために再抽出してくださいbuild.logを直接読み込まないでください- 他のログファイル(build_output*.log、error.log、error.*.log など)を読み込まないでください
- 抽出後は
last_error.logのみを分析してください
ステップ 2:エラーを読み取り、分類する
⚠️ 重大:OpenHarmony の出力ツリー内の out/<product>/ ディレクトリにある last_error.log のみを読み取ってください。
抽出後、エラーログは次の場所にあります:
out/<product>/last_error.log(build.log と同じディレクトリ)
抽出したエラーログを読み取ります:
# Check for last_error.log in common product directories
cat out/rk3568/last_error.log
# or for SDK (⚠️ SPECIAL CASE: out/sdk/ NOT out/ohos-sdk/)
cat out/sdk/last_error.log
# or for other products
cat out/rk3588/last_error.log
⚠️ 重大 — まず成功しているか確認してください:
分析を始める前に、ビルドが成功したかどうかを確認してください:
last_error.log に次のいずれかが含まれている場合:
build success, no errorno error- または同様の成功メッセージ
直ちに停止して、成功を報告してください:
## ✅ 构建成功
最新的构建已经成功完成,没有发现任何错误。
**构建状态**: 成功
**错误信息**: 无
建议: 您可以继续进行开发或测试工作。
次の作業は続行しないでください:
- ❌ error.log 内のエラーを検索する
- ❌ build_output*.log 内のエラーを検索する
- ❌ error.*.log ファイル内のエラーを検索する
- ❌ 過去のエラーを探す
- ✅ 停止して成功を報告する
実際にエラーがある場合に限り、分類を進めてください:
エラーの分類:
- コンパイルエラー(CXX タスク)— 構文、ヘッダーの不足、不完全型
- リンカーエラー(SOLINK/LINK タスク)— 未定義シンボル、ライブラリの不足
- ビルドシステムのエラー — GN/Ninja の設定上の問題
ステップ 3:既知のケースと照合する
references/ ディレクトリ内の過去のケースとエラーパターンを比較し、具体的な解決策を提示してください。
ケース照合の優先順位:
- エラーシグネチャで照合する(例:「undefined symbol」+「referred by」)
- エラーのコンテキストで照合する(例:「timepicker module」の使用)
- ファイルパターンで照合する(例:BUILD.gn の問題)
- 具体的なケースが見つからない場合は、一般的なガイダンスを提示する
ステップ 4:修正の推奨事項を提示する(自動変更は禁止)
重大:このステップでは推奨事項のみを提示します。Edit/Write ツールを使ってコードを変更しないでください。
エラーの種類と照合したケースに基づいて、次の内容を提示してください:
- 根本原因の分析
- 必要な具体的なファイル変更(正確な行番号とコードスニペットを含む)
- ビルドシステムの設定変更(正確な GN ファイルパスを含む)
- 検証手順
推奨される出力形式:
## 构建错误分析
### 错误类型
[编译错误/链接错误/构建系统错误]
### 错误位置
[文件路径:行号]
### 错误信息
[完整的错误消息]
### 根本原因
[详细解释为什么会出现这个错误]
### 修复建议
**文件**: [完整文件路径:行号]
**当前代码**:
```cpp
[错误代码片段]
次のように変更します:
[正确代码片段]
変更の説明:[このように変更する理由を説明]
検証手順
- [最初の検証操作]
- [2 番目の検証操作]
- [修正が成功したことを確認する方法]
参考
[関連する過去のケースまたはドキュメントへのリンク]
**DO NOT**:
- ❌ Use Edit tool to make changes
- ❌ Use Write tool to create/modify files
- ❌ Run build commands automatically
**DO**:
- ✅ Provide clear recommendations with file paths and line numbers
- ✅ Show before/after code snippets
- ✅ Explain why the fix works
- ✅ Suggest verification commands
## Error Patterns and Solutions
### Pattern 1: Undefined Symbol Errors
**Error signature:**
ld.lld: error: undefined symbol: <symbol_name>
referenced by :
**⚠️ CRITICAL ANALYSIS WORKFLOW (MUST FOLLOW IN ORDER):**
When encountering `ld.lld: error: undefined symbol:` linker errors, you MUST follow this sequence:
#### Step 1: Identify Which Library is Failing to Link
Check the error message to determine which library has the undefined symbol:
- Look for the target being built (e.g., `libace.z.so`, `libace_compatible.z.so`, `libarkoala_native_ani.so`, `libace_ndk.z.so`)
- Check which library is referencing the symbol (`>>> referenced by`)
#### Step 2: Apply the Correct Scenario Based on Library Type
**Scenario 1: libace/libace_compatible Main Library Cannot Find Symbol**
**Symptoms:**
- Error occurs when linking `libace.z.so` or `libace_compatible.z.so`
- Symbol is undefined in the main library itself
**Root Cause:**
- Symbol implementation is missing or not compiled
- Method exists but .cpp file not in BUILD.gn
**Solution:**
1. **Check if implementation exists:**
```bash
grep -r "SymbolName" --include="*.cpp" frameworks/
-
実装が存在する場合は、BUILD.gn を確認します:
grep -r "implementation_file.cpp" frameworks/*/BUILD.gn -
.cpp を BUILD.gn に追加します:
- 同じディレクトリにある他のファイルが追加されている箇所を見つけます
- 不足している .cpp を適切な source_set に追加します
- 重要:同じディレクトリ内のファイルを参照し、正しい source_set を確認します
- すべてのファイルが
ace_core_ng_source_setに含まれると決めつけないでください
-
他のファイルで確認します:
# Find where similar files in same directory are compiled find frameworks/core/components_ng/pattern/<pattern_name>/ -name "*.cpp" grep "similar_file.cpp" frameworks/core/BUILD.gn
シナリオ 2:libace/libace_compatible 依存関係を持つその他のライブラリのリンク
症状:
libaceまたはlibace_compatibleに依存するライブラリのリンク時にエラーが発生する- GN ファイルの
depsに libace/libace_compatible が含まれている - シンボルはメインライブラリから提供されるはずである
根本原因:
- シンボルはメインライブラリに存在するが、エクスポートされていない
- ACE_FORCE_EXPORT マクロが不足している
- シンボルが libace.map のホワイトリストに含まれていない
⚠️ 重大:まず非テンプレートかテンプレートかを判別してください
解決策を適用する前に、種類を判別してください:
undefined symbol error
↓
Is it a non-template class method?
→ YES: Use Non-Template Solution (Step 1A)
→ NO: Use Template Solution (Step 1B, see Scenario 3)
解決策(手順):
ステップ 1A:非テンプレートクラスのメソッドの場合 ⭐
⚠️ 確認 1:クラスに ACE_FORCE_EXPORT が付いていますか?
// Case A: Class WITHOUT ACE_FORCE_EXPORT
class PaddingPropertyF {
float Width() const;
};
// Case B: Class WITH ACE_FORCE_EXPORT
class ACE_FORCE_EXPORT PaddingPropertyF {
float Width() const;
};
ケース A(エクスポート指定のないクラス)の場合 — 推奨方法:
- ✅ ヘッダー(.h ファイル)のメソッド宣言に
ACE_FORCE_EXPORTを追加する - ❌ .cpp ファイルのメソッド定義には追加しない
- 理由:エクスポートを細かく制御でき、API の境界が明確になります
例(PaddingPropertyF のケース):
// In header file (.h) - Add ACE_FORCE_EXPORT to method declarations
class PaddingPropertyF {
ACE_FORCE_EXPORT float Width() const; // ← Add here
ACE_FORCE_EXPORT float Height() const; // ← Add here
};
// In implementation file (.cpp) - NO ACE_FORCE_EXPORT here
float PaddingPropertyF::Width() const
{
return left.value_or(0.0f) + right.value_or(0.0f);
}
float PaddingPropertyF::Height() const
{
return top.value_or(0.0f) + bottom.value_or(0.0f);
}
ケース B(エクスポート指定のあるクラス)の場合:
- ✅ 追加のエクスポート指定は不要 — クラスのエクスポート指定がすべてのメソッドに適用されます
- ❌ 個々のメソッドに
ACE_FORCE_EXPORTを追加しない - 理由:クラス全体がすでにエクスポートされています
例:
// In header file (.h) - Class already exported
class ACE_FORCE_EXPORT PaddingPropertyF { // ← Entire class exported
float Width() const; // Automatically exported
float Height() const; // Automatically exported
};
// In implementation file (.cpp) - No individual exports needed
float PaddingPropertyF::Width() const { /* implementation */ }
ステップ 1B:テンプレートクラスのメソッド/関数の場合(シナリオ 3 を参照)
テンプレート固有のルールが適用されます。詳しい手順については、シナリオ 3 を参照してください。
ステップ 2:細かく指定したシンボル名を libace.map に追加します ⭐
エクスポートを追加した後、シンボルをbuild/libace.mapに追加します。
# Check if symbol already in libace.map
grep "ClassName" build/libace.map
必須の変更:
- ⭐ 常に、きめ細かなシンボルパターンを優先してください — クラス全体ではなく、特定のメソッドをエクスポートします
- シンボルパターンを
build/libace.mapのホワイトリストに追加します - 可能な場合は、メソッド単位の粒度を使用します
例 — 通常のメソッド:
# In build/libace.map
{
global:
# ✅ FINE-GRAINED - Export specific methods (PREFERRED for non-template)
OHOS::Ace::PaddingPropertyF::Width*;
OHOS::Ace::PaddingPropertyF::Height*;
# ⚠️ ACCEPTABLE - Export entire class (if class has ACE_FORCE_EXPORT)
OHOS::Ace::PaddingPropertyF::*;
# ⚠️ DIFFERENT - Template patterns (use wildcards)
void?OHOS::Ace::StringUtils::StringSplitter*; # Template function
OHOS::Ace::NG::LayoutConstraintT*; # Template class
};
}
テンプレート以外でも、きめ細かな指定が必要な理由:
- ⭐ エクスポートするシンボルの範囲を最小限に抑えます — 必要なものだけをエクスポートします
- ⭐ API の境界が明確になります — 公開されるシンボルを明示できます
- ⭐ シンボルの競合を減らします — エクスポート範囲が小さいほど、競合も少なくなります
- ⭐ 依存関係を追跡しやすくなります — 他のモジュールで使用されているものを簡単に確認できます
- ⭐ セキュリティ — エクスポートされるシンボルの攻撃対象領域を制限します
よくある間違い(しないでください):
- ❌ 特定のメソッドだけが必要なのに、
ClassName::*;を使用する - ❌ 外部から使われていないプライベートメソッドをエクスポートする
- ❌ コンストラクターのワイルドカードがすべてのオーバーロードに一致することを忘れる
ステップ 3:シンボルのエクスポートを確認します:
nm -D out/rk3568/arkui/ace_engine/libace.z.so | grep SymbolName
- シンボルが表示されない場合、エクスポートに失敗しています
- ACE_FORCE_EXPORTとlibace.mapのエントリーを追加した後、再ビルドします
よくある間違い(しないでください):
- ❌ 特定のメソッドだけが必要なのに、
ClassName::*;を使用しないでください(⚠️ よくある間違い) - ❌ 未定義のコンストラクターしかないのに、クラスのメンバーすべてをエクスポートしないでください
- ❌ テンプレート以外のルールとテンプレートのルールを混在させないでください
- ❌ テンプレート以外のメソッドのエクスポートを.cppに追加しないでください
- ❌ クラスにすでに
ACE_FORCE_EXPORTがある場合、個別のメソッドのエクスポートを追加しないでください - ❌
__attribute__((visibility("default")))を直接使用しないでください
⚠️ よくある間違いの例 — 過剰なエクスポート:
// ❌ WRONG - Exports entire class when only constructors needed
OHOS::Ace::VelocityTracker::*; // Exports ALL members unnecessarily
// ✅ CORRECT - Fine-grained constructor export
OHOS::Ace::VelocityTracker::VelocityTracker*; // Only constructors
各パターンを使用する場面:
- 特定のメソッドには
ClassName::MethodName*;を使用します - すべてのコンストラクターのオーバーロードには
ClassName::ClassName*;を使用します - ほとんど、またはすべてのメンバーをエクスポートする場合にのみ、
ClassName::*;を使用します(まれです)
シナリオ 3:テンプレート関数のインスタンス化に関する問題
症状:
- 未定義のシンボルがテンプレート関数です
- テンプレートが前方宣言と.cppでの明示的なインスタンス化を使用しています
- エラーに不足している特定のテンプレート特殊化(例:
StringSplitter<Color>、TransformStrCase<std::string>)が示されています
根本原因:
- 新しい特殊化が明示的にインスタンス化されていません
- テンプレートの宣言または実装にエクスポートマクロがありません
- libace.mapにテンプレートのインスタンス化に対応するパターンがありません
解決方法(3つの手順が必要です):
ステップ 1:ヘッダー(.h)にextern template宣言を追加します
新しい特殊化に対するテンプレート宣言を明示的に追加します:
// In header file (.h)
extern template void StringSplitter<Color>(const std::string& source, char delimiter,
Color (*)(const std::string&), std::vector<Color>& out);
ステップ 2:テンプレート関数の宣言にACE_FORCE_EXPORTを追加します
// In header file (.h) - Template function declaration
template<class T>
ACE_FORCE_EXPORT
void StringSplitter(
const std::string& source, char delimiter, T (*func)(const std::string&), std::vector<T>& out);
ステップ 3:.cppに明示的なインスタンス化を追加します
// In implementation file (.cpp)
template void StringSplitter<Color>(const std::string& source, char delimiter,
Color (*)(const std::string&), std::vector<Color>& out);
ステップ 4:テンプレートの実装にACE_FORCE_EXPORTを追加します
// In implementation file (.cpp) - Template function implementation
template<class T>
ACE_FORCE_EXPORT
void StringSplitter(
const std::string& source, char delimiter, T (*func)(const std::string&), std::vector<T>& out)
{
// implementation
}
ステップ 5:libace.mapのホワイトリストに追加します
# In build/libace.map
{
global:
OHOS::Ace::StringUtils::*;
void?OHOS::Ace::StringUtils::StringSplitter*; # Match template instantiations
void?OHOS::Ace::StringUtils::TransformStrCase*; # Match template instantiations
};
⚠️ テンプレートに関する特記事項:
- テンプレートでは、宣言と実装の両方にエクスポートマクロが必要です
- 明示的なインスタンス化は.cppファイルに記述する必要があります
- libace.mapのパターンでは、テンプレートのインスタンス化に一致させるために
*ワイルドカードを使用する必要があります - パターンの例:
void?OHOS::Ace::StringUtils::StringSplitter*;
シナリオ 4:特殊なライブラリ(まれなケース)
症状:
- ライブラリがlibace/libace_compatibleに依存していません
- ただし、メインライブラリのユーティリティ関数を使用しています(以前はインラインでしたが、現在は.cppにあります)
- 以下の特定のライブラリにのみ適用されます:
libarkoala_native_ani.solibace_ndk.z.so
根本原因:
- ユーティリティ関数がインライン実装から.cppの実装に変更されました
- ライブラリがメインライブラリをリンクしていないため、シンボルを使用できません
- ローカルでのコンパイルが必要な特殊なケースです
解決方法:
- ユーティリティの.cppファイルを、ライブラリのBUILD.gnのsourcesに直接追加します
libarkoala_native_aniの例:
# In: frameworks/bridge/arkts_frontend/koala_projects/arkoala-arkts/arkui-ohos/src/ani/native/BUILD.gn
ohos_shared_library("arkoala_native_ani") {
sources = [
"//foundation/arkui/ace_engine/frameworks/base/utils/string_utils.cpp",
"UINode/uinode_module_methods.cpp",
# ... other sources
]
}
ace_ndkの例:
# In: interfaces/native/BUILD.gn
ohos_shared_library("ace_ndk") {
sources = [
"//foundation/arkui/ace_engine/frameworks/base/utils/string_utils.cpp",
"//foundation/arkui/ace_engine/frameworks/core/accessibility/native_interface_accessibility_impl.cpp",
# ... other sources
]
}
⚠️ 重要:
- この解決方法は
libarkoala_native_aniとace_ndkにのみ適用されます - 他のライブラリでこのパターンを使用しないでください
- これらのライブラリはユーティリティ関数を使用しますが、メインライブラリをリンクしていません
シナリオ 5:テンプレートクラスのメソッドのエクスポートに関する問題 症状:
libaceまたはlibace_compatibleに依存するライブラリのリンク時にエラーが発生する- 未定義シンボルが、明示的なインスタンス化を伴うテンプレートクラスのメソッドである
- エラーに
undefined symbol: ClassName<T>::MethodName(params)と表示される - テンプレートクラスの構造体宣言には
ACE_FORCE_EXPORTが付いているが、メソッドはエクスポートされない
エラー例:
ld.lld: error: undefined symbol: OHOS::Ace::NG::LayoutConstraintT<float>::UpdateSelfMarginSizeWithCheck(OHOS::Ace::NG::OptionalSize<float> const&)
>>> referenced by slider_layout_algorithm.cpp:212
根本原因:
- テンプレートクラスの構造体宣言に
ACE_FORCE_EXPORTが付いている(例:struct ACE_FORCE_EXPORT LayoutConstraintT) - しかし、.cpp ファイル内の個々のメソッド実装に
ACE_FORCE_EXPORTマクロが付いていない - テンプレートの明示的なインスタンス化では、個々のメソッドは自動的にエクスポートされない
- エクスポートする必要がある各メソッドの実装に、マクロを付ける必要がある
解決方法:
手順 1:.cpp のメソッド実装に ACE_FORCE_EXPORT を追加する
エクスポートする必要がある各テンプレートメソッドの実装に、ACE_FORCE_EXPORT を追加します。
// In implementation file (.cpp)
template<typename T>
ACE_FORCE_EXPORT // ← Add this macro before return type
bool LayoutConstraintT<T>::UpdateSelfMarginSizeWithCheck(const OptionalSize<T>& size)
{
if (selfIdealSize == size) {
return false;
}
return selfIdealSize.UpdateSizeWithCheck(size);
}
template<typename T>
ACE_FORCE_EXPORT // ← Add this macro before return type
bool LayoutConstraintT<T>::UpdateMaxSizeWithCheck(const SizeT<T>& size)
{
if (maxSize == size) {
return false;
}
return maxSize.UpdateSizeWhenSmaller(size);
}
⚠️ テンプレートメソッドのエクスポートに関する注意事項:
- 構造体宣言のエクスポートだけでは不十分です:
struct ACE_FORCE_EXPORT LayoutConstraintTがあっても、すべてのメソッドが自動的にエクスポートされるわけではありません - 各メソッドにマクロが必要です:エクスポートする必要がある .cpp 内の各メソッド実装に、
ACE_FORCE_EXPORTが必要です - マクロは戻り値の型の前に置きます:マクロは
template<>と戻り値の型の間に置きます - ヘッダーには不要です:ヘッダーファイル内のメソッド宣言に
ACE_FORCE_EXPORTを追加しないでください(構造体宣言で十分です)
手順 2:libace.map にワイルドカードパターンがあることを確認する
libace.map に、ワイルドカードを含むテンプレートクラスが指定されていることを確認します。
# Check libace.map for template class pattern
grep "LayoutConstraintT" build/libace.map
期待される出力:
OHOS::Ace::NG::LayoutConstraintT*;
⚠️ libace.map のワイルドカード規則:
- ワイルドカードを含む、引用符で囲まないパターンを使用します:
OHOS::Ace::ClassName::*; - 使用しないでください:引用符で囲んだワイルドカードパターン(
"OHOS::Ace::ClassName<T>::*;")。ワイルドカードが機能しません - 引用符で囲まないワイルドカードは、すべてのテンプレートインスタンス化に自動的に一致します
- すべてのメソッドに一致させるには、末尾に
*を使用します:OHOS::Ace::NG::LayoutConstraintT*;
手順 3:シンボルのエクスポートを確認する
# Check if symbol is exported in shared library
nm -D out/rk3568/arkui/ace_engine/libace.z.so | grep LayoutConstraintT
期待される出力(エクスポートされたシンボルが表示されるはずです):
0000000000000000 T _ZN2OHOS3Ace2NG14LayoutConstraintTIfE25UpdateSelfMarginSizeWithCheckERKNS0_11OptionalSizeIfEE
⚠️ よくある間違い:
- ❌
struct ACE_FORCE_EXPORT ClassNameだけに頼ること。メソッドには不十分です - ❌ .cpp 内の各メソッド実装にマクロを追加し忘れること
- ❌ ヘッダー内のメソッド宣言にマクロを追加すること(不要です。構造体宣言だけで十分です)
- ❌ libace.map で引用符で囲んだパターンを使用すること:
"ClassName<T>::*"は一致しません - ❌ libace.map に特定のテンプレートインスタンス化を追加すること。代わりにワイルドカードを使用してください
✅ 正しい方法:
- ✅ .cpp 内の各テンプレートメソッド実装に
ACE_FORCE_EXPORTを追加する - ✅ マクロを
template<>と戻り値の型の間に置く - ✅ ヘッダー内の構造体宣言には
ACE_FORCE_EXPORTを付けたままにする - ✅ libace.map で引用符で囲まないワイルドカードを使用する:
OHOS::Ace::NG::ClassName*;
ステップ 3:修正を確認する
適切な解決策を適用した後:
# 1. Navigate to OpenHarmony root and rebuild
cd <openharmony_root>
./build.sh --product-name rk3568 --build-target ace_engine
# 2. Check if symbol is now exported (for scenarios 2-3)
nm -D out/rk3568/arkui/ace_engine/libace.z.so | grep SymbolName
# 3. Extract new errors
foundation/arkui/ace_engine/.claude/skills/build-error-analyzer/script/extract_last_error.sh out/rk3568/build.log
# 4. Check result
cat out/rk3568/last_error.log
# For SDK builds (⚠️ use out/sdk/ directory):
# 1. Build SDK: ./build.sh --product-name ohos-sdk --ccache
# 2. Extract errors: foundation/arkui/ace_engine/.claude/skills/build-error-analyzer/script/extract_last_error.sh out/sdk/build.log
# 3. Check result: cat out/sdk/last_error.log
よくある間違い(禁止事項):
⚠️ 重大 - キャッシュの消去は禁止されています:
- ❌ 絶対に thinlto-cache の消去を提案しないでください(99.9%の場合、原因ではありません)
- ❌ 絶対に llvmcache-* ディレクトリの消去を提案しないでください
- ❌ 絶対に out/obj ディレクトリの削除を提案しないでください
- ❌ 絶対に out ディレクトリ全体の削除を提案しないでください
- ❌ 絶対に 最初の解決策として「クリーンビルド」を提案しないでください
- ❌ キャッシュの消去は本当の問題を隠し、膨大な再ビルド時間を浪費します
- ✅ 必ず 実際のエラーを分析し、根本原因を修正してください
その他のよくある間違い:
- ❌ シナリオ分析を省略しないでください
- ❌ ACE_FORCE_EXPORT の代わりに
__attribute__((visibility("default")))を使用しないでください - ❌ テンプレートの宣言と実装の両方にエクスポート指定を忘れずに付けてください
- ❌ 指定された 2 つのライブラリ以外で、シナリオ 4(ローカルコンパイル)を使用しないでください
- ❌ テンプレートクラスのメソッドについて、
struct ACE_FORCE_EXPORT ClassNameだけに頼らないでください(シナリオ 5) - ❌ libace.map でワイルドカードを含む引用符付きパターンを使用しないでください:
"ClassName<T>::*"は機能しません(シナリオ 5)
✅ 必ず実施すること:
- ✅ どのライブラリでエラーが発生しているか特定する
- ✅ 実装が存在し、コンパイルされているか確認する
- ✅ ヘッダー宣言に ACE_FORCE_EXPORT があるか確認する
- ✅ libace.map のホワイトリストを確認する
- ✅ テンプレートの場合:宣言、実装、インスタンス化を確認する
- ✅ ライブラリの種類に応じて正しいシナリオを使用する
- ✅ テンプレートクラスのメソッドの場合:.cpp 内の各メソッド実装に ACE_FORCE_EXPORT を追加する(シナリオ 5)
- ✅ libace.map で引用符なしのワイルドカードパターンを使用する:
OHOS::Ace::NG::ClassName*;(シナリオ 5)
パターン 2:不完全型エラー
エラーの形式:
error: member access into incomplete type 'const ClassName'
error: invalid use of incomplete type 'class ClassName'
error: member access into incomplete type 'OHOS::Ace::Animator'
rawPtr_->IncRefCount();
よくある原因:
- ヘッダーのインクルードが不足している
- 前方宣言はあるが、完全な定義がない
- 適切な実装の分離をせずに、クラスメンバーとして RefPtr を使用している
- ヘッダーファイルの最適化に関する問題
⚠️ 特別なケース:クラスメンバーとしての RefPtr
クラスメンバーとして RefPtr<T> または WeakPtr<T> を使用している箇所で不完全型エラーが発生した場合:
ヘッダーファイルに完全なインクルードを単に追加しないでください。代わりに:
- ヘッダーで前方宣言を維持する:
class Animator; - 特別なメンバー関数をヘッダーで宣言する(
= defaultは付けない):AnimatableColor(); // Declaration only ~AnimatableColor(); // Declaration only AnimatableColor(const AnimatableColor& color); // Declaration only - .cpp ファイルで
= defaultを付けて実装する:// animatable_color.cpp #include "core/animation/animator.h" // Full definition here AnimatableColor::AnimatableColor() = default; AnimatableColor::~AnimatableColor() = default; AnimatableColor::AnimatableColor(const AnimatableColor& color) = default;
参照先:詳細な解決方法については references/forward-declaration-refptr-member.md を参照してください
パターン 3:再定義エラー
エラーの形式:
error: redefinition of 'symbol_name'
よくある原因:
- 複数の翻訳単位で定義されている
- ヘッダーのみの定義に inline/constexpr が不足している
- ODR 違反
過去の事例
すべての事例は、問題の種類ごとに references/ ディレクトリに整理されています。
事例 1:未定義シンボル - .cpp ファイルの不足
場所:references/undefined-symbol-missing-cpp.md
エラーの形式:
ld.lld: error: undefined symbol: OHOS::Ace::TextTheme::Builder::Build
ld.lld: error: undefined symbol: OHOS::Ace::AdvancedTextStyle::GetGradient
よくある原因:
- 新しい .cpp ファイルを作成したが、BUILD.gn に追加していない
- ファイルをコンポーネントの BUILD.gn に追加したが、source_set に追加していない
- libace.z.so は ace_core_ng ライブラリのみをリンクする
解決策:.cpp をコンポーネントの BUILD.gn と frameworks/core/BUILD.gn の xx_ng_source_set の両方に追加します
事例 2:シンボルのエクスポート - ACE_FORCE_EXPORT の不足
場所:references/symbol-export-ace-force-export.md
エラーの形式:
ld.lld: error: undefined symbol: OHOS::Ace::NG::DialogTypeMargin::UpdateDialogMargin
>>> referenced by dialog_button.cpp (timepicker module)
よくある原因:
- 他のモジュールで使用されているシンボルに、エクスポート指定がない
- ヘッダー宣言に ACE_FORCE_EXPORT がない
解決策:ヘッダー宣言に ACE_FORCE_EXPORT を追加し、build/libace.map に追加します
事例 3:シンボルのエクスポート - libace.map のホワイトリスト
場所:references/symbol-export-libace-map.md
エラーの形式:
ld.lld: error: undefined symbol: OHOS::Ace::NG::ClassName::MethodName
コンテキスト:シンボルに ACE_FORCE_EXPORT があるにもかかわらず、エクスポートされない
よくある原因:
- シンボルが build/libace.map のホワイトリストにない
- バージョンスクリプト内のシンボル形式が正しくない
解決策:正しい形式でシンボルを build/libace.map に追加します:OHOS::Ace::NG::ClassName::MethodName*;
ケース 4:再定義 - inline constexpr
場所: references/redefinition-error-constexpr.md
エラーの特徴:
error: redefinition of 'DRAG_BACKGROUND_OPACITY'
error: redefinition of 'URL_DISA_OPACITY'
一般的な原因:
- 定数がヘッダー(inline constexpr)と .cpp の両方で定義されている
- ODR(One Definition Rule)違反
解決方法: .cpp から重複する定義を削除し、ヘッダーの inline constexpr のみを残します
ケース 5:ビルドシステム - ace_core_ng_source_set
場所: references/build-system-ace-core-ng-source-set.md
エラーの特徴:
ld.lld: error: undefined symbol
状況: ファイルがコンポーネントの BUILD.gn に存在するにもかかわらず、シンボルが未定義のまま
一般的な原因:
- ファイルが frameworks/core/BUILD.gn の ace_core_ng_source_set に含まれていない
- libace.z.so は ace_core_ng ライブラリのみをリンクしている
解決方法: frameworks/core/BUILD.gn の ace_core_ng_source_set テンプレートにファイルを追加します
ケース 6:RefPtr メンバー - 前方宣言の最適化
場所: references/forward-declaration-refptr-member.md
エラーの特徴:
error: member access into incomplete type 'OHOS::Ace::Animator'
rawPtr_->IncRefCount();
note: in instantiation of member function 'OHOS::Ace::RefPtr<OHOS::Ace::Animator>::RefPtr'
状況: クラスのメンバー変数として RefPtr<Animator> または WeakPtr<T> を使用している
一般的な原因:
- 特殊メンバー関数(コンストラクター/デストラクター/コピーコンストラクター)がヘッダー内で
= defaultによって定義されている - コンパイラーがヘッダー内でこれらの関数をインスタンス化するため、完全な型定義が必要になる
- よくある誤り:前方宣言を取り消し、完全なインクルードを追加する
正しい解決方法: 前方宣言を取り消さないでください。実装を分離します:
- ヘッダーの前方宣言を維持します:
class Animator; - 特殊メンバー関数をヘッダーで宣言します(
= defaultは付けません) - .cpp ファイルで
= defaultを指定して実装します - .cpp ファイルに完全な定義をインクルードします
メリット: ヘッダーの依存関係が減り、コンパイルが高速化します(例では 57% 改善)
ケース 7:テストのリンク - ソースファイルの不足
場所: references/test-missing-source-files.md
エラーの特徴:
ld.lld: error: undefined symbol: OHOS::Ace::ClassName::MethodName(...)
>>> referenced by test_file.cpp:123
状況: テスト実行可能ファイル(例:xxx_unittest)をリンクしている
一般的な原因:
- テストの BUILD.gn の sources に必要な .cpp ファイルがない
- ヘッダーの最適化によって実装が .cpp に移動された(例:StringUtils、LogWrapper)
- 新しい実装ファイルがビルドシステムに追加されていない
よく不足するファイル:
log_wrapper.cpp- LogWrapper のメソッド(JudgeLevel、GetBriefFileName、PrintLog)string_utils.cpp- StringUtils のメソッド(StringSplitter、TransformStrCase、StrToInt)layout_constraint.cpp- LayoutConstraintT のテンプレートメソッドmeasure_property.cpp- PaddingPropertyF、MeasureProperty のメソッド
解決方法: テストのテンプレート種別に応じて、適切な sources に不足している .cpp ファイルを追加します:
- ohos_unittest(ace_base なし):テストの sources に追加します
- ohos_unittest(ace_base あり):ace_base の source_set に追加します
- ace_unittest:ace_base の source_set に追加します(デフォルトの依存関係)
⚠️ 重要:ソースファイルのみを追加し、cflags/configs/defines は変更しないでください
ケース 8:Struct の RefPtr メンバー - ヘルパーメソッドパターン
場所:references/forward-declaration-struct-helper-method.md
エラーシグネチャ:
error: member access into incomplete type 'OHOS::Ace::PixelMap'
shadowInfo.pixelMap->GetPixelMapSharedPtr()
^
note: forward declaration of 'OHOS::Ace::PixelMap'
class PixelMap;
コンテキスト:純粋なデータ構造(POD struct)が RefPtr<T> メンバーを含み、-> によるアクセスを必要とします
一般的な原因:
- Struct が
RefPtr<T>メンバー変数を含んでいます - コードでスマートポインターに対して
->演算子を呼び出す必要があります pixelMap->Method()を直接呼び出すと、不完全型エラーが発生します- Struct 自体に .cpp ファイルがないため、Case 6 の方法を使用できません
根本原因:
RefPtr<T>::operator->()はLifeCycleCheckable::PtrHolder<T>を返しますPtrHolderのコンストラクター/デストラクターがusingCount_にアクセスするには、完全な型定義が必要です- 純粋なデータ構造には、独自の実装ファイルがありません
解決策:型へのアクセスをカプセル化するヘルパーメソッドを追加します:
- Struct でヘルパーメソッドを宣言します:
std::shared_ptr<::OHOS::Media::PixelMap> GetPixelMapSharedPtr() const; - 新しい実装ファイルを作成します(例:
interaction_data.cpp) - 完全な include を指定して、.cpp でヘルパーメソッドを実装します:
#include "base/image/pixel_map.h" ->による直接アクセスの代わりに、ヘルパーメソッドを呼び出すよう使用箇所を更新します- BUILD.gn のソースセットに新しい .cpp を追加します
高度なシナリオ(ヘルパーメソッドだけでは不十分な場合):
- Struct 自体に .cpp ファイルがあり、
std::vectorで使用されています std::vector::operator=がコピーを行うため、完全な型が必要です- 解決策:完全な前方宣言の最適化に切り替えます(Case 6 と同様)
forward-declaration-struct-helper-method.mdの「高度なシナリオ」セクションを参照してください
要点:
- 名前空間の混同を避けるため、完全修飾名
::OHOS::Media::PixelMapを使用します - ヘッダーでは前方宣言を維持し、完全な定義は .cpp のみに置きます
ケース 9:名前空間の型解決 - 修飾子の欠落
場所:references/namespace-ambiguous-type-resolution.md
エラーシグネチャ:
error: no member named 'DragEvent' in namespace 'OHOS::Ace'
using OnDragStartFunc = std::function<DragDropBaseInfo(const RefPtr<OHOS::Ace::DragEvent>&, const std::string&)>;
~~~~~~~~~~~~~^
コンテキスト:親名前空間で定義された型を、子名前空間で使用しています
一般的な原因:
- 型
DragEventはOHOS::Ace名前空間で定義されています OHOS::Ace::NG子名前空間のコードで使用しています- git diff に名前空間プレフィックスの削除が示されています(例:
- OHOS::Ace::DragEvent) - 誰かが完全修飾された名前空間プレフィックスを削除しました
根本原因:
- 型は親名前空間(例:
OHOS::Ace::DragEvent)で定義されています - 子名前空間(例:
OHOS::Ace::NG)で使用されています - 名前空間プレフィックスを削除すると、コンパイラーは誤った名前空間を検索します
- 誤った名前空間での前方宣言では解決しません
正しい解決策(優先順位順):
-
完全な名前空間修飾子を維持する(推奨):
// In OHOS::Ace::NG namespace using OnDragStartFunc = std::function<DragDropBaseInfo( const RefPtr<OHOS::Ace::DragEvent>&, // ✅ Keep full namespace const std::string&)>; -
正しい名前空間で前方宣言を追加する:
// In header, before OHOS::Ace::NG namespace namespace OHOS::Ace { class DragEvent; // ✅ Forward declare in correct namespace } // In OHOS::Ace::NG namespace using OnDragStartFunc = std::function<DragDropBaseInfo( const RefPtr<OHOS::Ace::DragEvent>&, // Still need full namespace const std::string&)>; -
ヘッダー依存関係を追加する(完全な型が必要な場合):
#include "core/components_ng/event/drag_event.h" // In OHOS::Ace::NG namespace using OnDragStartFunc = std::function<DragDropBaseInfo( const RefPtr<OHOS::Ace::DragEvent>&, // Still use full namespace const std::string&)>;
⚠️ 重要:
- 名前空間プレフィックスを削除してはいけません
- 前方宣言は、型定義と同じ名前空間になければなりません
- 型が
OHOS::Aceにある場合、前方宣言もOHOS::Aceに置く必要があります - 使用するコードは子名前空間に置けますが、親名前空間を参照する必要があります
要点:
- 完全な型定義を確認します(前方宣言だけではありません)
- 前方宣言の名前空間を型定義の名前空間に合わせます
- include を追加するより、完全修飾名を維持する方法を優先します
- ヘルパーメソッドは完全な型へのアクセスをカプセル化します
- クリーンな API を維持しながら、ヘッダー依存関係を減らします
使用する場面:
- ✅ スマートポインターメンバーを持つ純粋なデータ構造(POD struct)
- ✅ スマートポインターを
->または*で逆参照する必要がある場合 - ✅ 既存の Struct に .cpp を追加できない場合
- ❌ 独自の .cpp ファイルを持つクラス(代わりに Case 6 を使用)
- ❌ コンストラクター/デストラクターだけが必要な場合(代わりに Case 6 を使用)
ケース 9:LTO の仮想 thunk - libace.map のエクスポート
場所:references/lto-virtual-thunk-libace-map-export.md
エラーシグネチャ:
ld.lld: error: undefined symbol: virtual thunk to OHOS::Ace::TouchEventTarget::~TouchEventTarget()
>>> referenced by ld-temp.o
>>> lto.tmp:(construction vtable for OHOS::Ace::TouchEventTarget-in-OHOS::Ace::V2::ListScrollBarController)
>>> referenced by ld-temp.o
>>> lto.tmp:(construction vtable for OHOS::Ace::TouchEventTarget-in-OHOS::Ace::VerticalDragRecognizer)
コンテキスト:仮想関数を持つクラスが基底クラスとして使用され、デストラクターがヘッダーで宣言されて .cpp で実装されている(前方宣言の最適化)。LTO(リンク時最適化)が有効
一般的な原因:
- クラスが仮想関数を持ち、基底クラスとして使用されている
- 前方宣言の最適化:デストラクターが .cpp ファイルにある(インラインではない)
- LTO が派生クラス用の仮想 thunk を作成する
- 仮想 thunk シンボルがライブラリからエクスポートされていない
根本原因:
- LTO がリンク時に仮想関数テーブルを最適化する
- 派生クラス向けに this ポインターを調整する 仮想 thunk シンボルを作成する
- これらの仮想 thunk シンボルはリンク時に利用可能である必要がある
- デストラクターがインラインでない場合、LTO が生成した仮想 thunk がエクスポートされないことがある
- よくある誤り:インラインデストラクター(ヘッダー内の
= default)に戻す
解決策:前方宣言の最適化を維持し、libace.map にエクスポートを追加します:
- デストラクターを .cpp ファイルに置いたままにする:ヘッダーには
~TouchEventTarget() override;を記述し、実装は .cpp に置く - libace.map に追加する:
OHOS::Ace::TouchEventTarget::*;- クラスの全シンボルをエクスポートvirtual?thunk?to?OHOS::Ace::TouchEventTarget::*;- LTO の仮想 thunk をエクスポート
- ヘッダー内のインライン定義に戻さないでください
要点:
- 2 つのエクスポートパターンが必要です:
ClassName::*;とvirtual?thunk?to::ClassName::*; - ワイルドカード
*は仮想 thunk を含むすべてのメソッドに一致します - 前方宣言の最適化を維持します
- LTO のためにヘッダーの最適化を犠牲にする必要はありません
この方法が機能する理由:
- コンパイルフェーズ:ヘッダーでは前方宣言を使用し、.cpp に実装を置く
- LTO リンクフェーズ:LTO が仮想関数テーブルを最適化し、仮想 thunk を作成する
- シンボルのエクスポート:libace.maps のパターンが仮想 thunk に一致し、動的シンボルテーブルにエクスポートする
- 実行時:派生クラスの仮想関数テーブルが仮想 thunk を正しく参照できる
使用する場面:
- ✅ 仮想関数を持ち、基底クラスとして使用されるクラス
- ✅ 前方宣言の最適化を使用している(デストラクターがインラインではない)
- ✅ リンカーエラー:「undefined symbol: virtual thunk to ClassName::~ClassName()」
- ✅ LTO(リンク時最適化)を使用している
- ❌ 基底クラスとして使用されないクラス(仮想 thunk は生成されません)
- ❌ デストラクターがすでにヘッダーでインライン定義されている(維持する最適化がありません)
- ❌ LTO を使用していない(従来のコンパイルでは仮想 thunk は作成されません)
よくある誤り:
- ❌ インラインデストラクターに戻す(前方宣言の最適化が失われます)
- ❌ 仮想 thunk のパターンを使わず、
ClassName::*;だけをエクスポートする - ❌ libace.map のエントリーを追加し忘れる
ケース 10:MinGW - dllexport 宣言の不一致
場所:references/mingw-dllexport-declaration-mismatch.md
エラーの特徴:
error: redeclaration of 'OHOS::Ace::NG::PaddingPropertyT::SetEdges' cannot add 'dllexport' attribute
void PaddingPropertyT<T>::SetEdges(const T& leftValue, const T& rightValue, const T& topValue, const T& bottomValue)
^
note: previous declaration is here
void SetEdges(const T& leftValue, const T& rightValue, const T& topValue, const T& bottomValue);
^
コンテキスト:MinGW/Windows プラットフォームでのコンパイルのみ
主な特徴:
- MinGW/Windows のビルドでのみ発生します(Linux/MacOS では発生しません)
- エラーメッセージ:「redeclaration cannot add 'dllexport' attribute」
- ヘッダーの宣言と .cpp の実装で、エクスポート属性が一致していません
- 通常、テンプレートメソッドに
ACE_FORCE_EXPORTを追加した後に発生します
よくある原因:
- ヘッダーファイルのメソッド宣言に
ACE_FORCE_EXPORTがありません - 実装ファイルには
ACE_FORCE_EXPORTがあるものの、ヘッダーにはありません - オーバーロードされたメソッドの一部にのみエクスポート属性があり、一致していません
- MinGW では宣言と定義の厳密な一致が必要です
根本原因:
- MinGW の DLL エクスポート規則では、宣言と定義のエクスポート属性が完全に一致している必要があります
- ヘッダーに
__declspec(dllexport)がなく、実装にある場合、コンパイルに失敗します - 可視性属性の扱いがより寛容な Linux/MacOS とは異なります
解決策:ヘッダー内のオーバーロードされたメソッド宣言すべてに ACE_FORCE_EXPORT を追加します:
// Header file - add to ALL declarations
ACE_FORCE_EXPORT void SetEdges(const T& padding);
ACE_FORCE_EXPORT void SetEdges(const T&, const T&, const T&, const T&); // ✅ Don't forget
ACE_FORCE_EXPORT bool operator==(const PaddingPropertyT& value) const;
ACE_FORCE_EXPORT bool operator!=(const PaddingPropertyT& value) const; // ✅ Don't forget
重要なポイント:
- .cpp の実装だけでなく、ヘッダーファイルの宣言にも追加します
- オーバーロードされたメソッドすべてに付けていることを確認します(付け忘れがないようにします)
- 実装ファイルにも
ACE_FORCE_EXPORTが必要です - MinGW プラットフォーム(Windows ビルド)でテストします
使用する場面:
- ✅ MinGW/Windows でのコンパイルエラー
- ✅ エラー:「cannot add 'dllexport' attribute」
- ✅ エクスポート属性を持つテンプレートメソッド
- ❌ Linux/MacOS のみの場合(規則が異なります)
- ❌ リンクエラーの場合(これはコンパイルエラーです)
予防チェックリスト:
- ✅ エクスポートが必要であることを確認します(モジュール間で使用する場合)
- ✅ ヘッダーの宣言すべてに
ACE_FORCE_EXPORTを追加します - ✅ .cpp の定義すべてに
ACE_FORCE_EXPORTを追加します - ✅ オーバーロードされたバージョンをすべて確認します(見落としがないようにします)
- ✅ MinGW プラットフォームでコンパイルをテストします
ケース 11:std::function<RefPtr> の前方宣言
場所:references/case-std-function-refptr-forward-declaration.md
エラーシグネチャ:
error: no member named 'DragEvent' in namespace 'OHOS::Ace'
using OnDragStartFunc = std::function<DragDropBaseInfo(const RefPtr<OHOS::Ace::DragEvent>&, const std::string&)>;
~~~~~~~~~~~^
コンテキスト:パラメーター型として RefPtr<T> を使用する std::function テンプレート
主な特徴:
RefPtr<T>はサイズが固定されたポインターラッパーですusingエイリアスでは、テンプレートはすぐにはインスタンス化されませんRefPtr<T>のサイズが既知であるため、前方宣言が可能です- 実際のテンプレートのインスタンス化は .cpp ファイルで行われます
一般的な原因:
std::functionには完全な型定義が必要だという誤解- テンプレートのインスタンス化が実際に行われるタイミングへの理解不足
- 型エイリアス宣言とテンプレートのインスタンス化の混同
根本原因:
RefPtr<T>のサイズは固定(ポインターサイズ)なので、前方宣言が可能ですusing OnDragStartFunc = ...は型エイリアスにすぎず、インスタンス化ではありません- テンプレートのインスタンス化は、.cpp で型が実際に使用されたときにのみ行われます
解決策:ヘッダーでは前方宣言を使用し、.cpp では完全な定義を使用します:
// Header file (gesture_event_hub.h)
namespace OHOS::Ace {
class DragEvent; // ✅ Forward declaration only
}
namespace OHOS::Ace::NG {
// Type alias - NO template instantiation here
using OnDragStartFunc = std::function<DragDropBaseInfo(
const RefPtr<OHOS::Ace::DragEvent>&, // ✅ Works with forward decl
const std::string&)>;
}
// Implementation file (gesture_event_hub.cpp)
#include "core/gestures/drag_event.h" // ✅ Full definition here
// Actual usage - template instantiation happens here
OnDragStartFunc callback = ...;
主な原則:
- RefPtr は単なるポインターラッパーです(サイズ固定)
- using エイリアス ≠ テンプレートのインスタンス化
- 実際に使用されたときに、.cpp でインスタンス化されます
- ヘッダー:前方宣言で十分です
- 実装:完全な定義が必要です
使用する場合:
- ✅ ヘッダー内の
std::function<RefPtr<T>>(型エイリアス) - ✅ ヘッダー内の
std::vector<RefPtr<T>>(インスタンス化されません) - ✅ RefPtr を返す、または引数に取る関数宣言
- ❌ ヘッダー内での T メンバーへのアクセス(完全な定義が必要)
- ❌ T メンバーを使用するインラインメソッド(完全な定義が必要)
関連ケースとの比較:
- ケース 6:クラスメンバーとしての RefPtr → ✅ 前方宣言が可能
- ケース 8:アクセスを伴う構造体内の RefPtr → ⚠️ ヘルパーメソッドが必要
- ケース 11:std::function<RefPtr> → ✅ 前方宣言が可能(本ケース)
メリット:
- ヘッダーの依存関係を削減できます
- コンパイルを高速化できます
- 名前空間の境界をより明確にできます
- 最適化の成果を維持できます
実例:OpenHarmony ACE Engine - gesture_event_hub.h の最適化では、std::function<RefPtr<OHOS::Ace::DragEvent>> と前方宣言を組み合わせて正常に使用しています
分析コマンド
シンボルの存在を確認
# Search for symbol definition in codebase
grep -r "SymbolName" --include="*.h" --include="*.cpp" frameworks/
# Check if .cpp file exists
find frameworks/ -name "implementation_file.cpp"
ビルド構成を確認
# Check if file in BUILD.gn
grep -r "implementation_file.cpp" frameworks/*/BUILD.gn
# Check ace_core_ng source set
grep -A 100 "ace_core_ng_source_set" frameworks/core/BUILD.gn | grep "implementation_file.cpp"
シンボルのエクスポートを確認
# Check if symbol exported in shared library
nm -D out/rk3568/arkui/ace_engine/libace.z.so | grep SymbolName
# Check libace.map
grep "SymbolName" build/libace.map
トラブルシューティングガイド
新しい .cpp ファイルの場合
- 実装ファイル(.cpp)を作成します
- コンポーネントの BUILD.gn に追加します
- frameworks/core/BUILD.gn の ace_core_ng_source_set に追加します(libace.z.so で使用する場合)
- シンボルがモジュール間で使用される場合:
- ヘッダー宣言に ACE_FORCE_EXPORT を追加します
- build/libace.map のホワイトリストに追加します
ヘッダー最適化に関する問題の場合
- 前方宣言が誤って使用されていないか確認します
- 必要な場所で完全な型を利用できることを確認します
- ヘッダー内のみの定数には inline constexpr を使用します
- ODR 違反を避けます
リンクエラーの場合
- エラーメッセージから不足しているシンボルを特定します
- シンボルの定義箇所を探します
- 実装がコンパイルされていることを確認します(BUILD.gn 内)
- モジュール間で使用される場合は、シンボルの可視性/エクスポートを確認します
- リンク順序と依存関係を確認します
ベストプラクティス
⚠️ 重大なルール - LTO キャッシュを絶対にクリアしないでください。また、「クリーンビルド」を提案しないでください
⚠️ 絶対禁止 - ビルドエラーが発生した場合は、どのような場合でも次の行為をしないでください。
ルールの説明: いかなる状況でも、最初の解決策または主な解決策として、LTO キャッシュのクリア、ビルドディレクトリの削除、「クリーンビルド」の実行を提案しないでください。
厳禁事項:
- ❌
thinlto-cacheディレクトリのクリアを絶対に提案しないでください - ❌
llvmcache-*ディレクトリのクリアを絶対に提案しないでください - ❌
out/objディレクトリの削除を絶対に提案しないでください - ❌
out/ディレクトリ全体の削除を絶対に提案しないでください - ❌
rm -rf out/の実行を絶対に提案しないでください - ❌ 「クリーンビルド」や「最初から再ビルド」を絶対に提案しないでください
- ❌ リンカーエラーの原因を LTO キャッシュのせいに絶対にしないでください
- ❌ 「キャッシュの問題かもしれないので、クリアしてみてください」と絶対に言わないでください
禁止されている理由:
- LTO キャッシュの問題は極めてまれです - ビルドエラーの 99.9% はコードや設定の問題であり、キャッシュの問題ではありません
- 本当の問題を隠してしまいます - キャッシュをクリアしても根本原因は解決されず、一時的に問題が見えなくなるだけです
- 膨大な時間を無駄にします - フルビルドには何時間もかかりますが、インクリメンタルビルドなら数分で済みます
- インクリメンタルコンパイルのメリットが失われます - 以降のビルドが遅くなります
- 再現性が損なわれます - ビルド状態を破壊することで、デバッグが難しくなります
- ずさんな診断です - キャッシュのクリアを提案することは、エラーの分析が不十分であることを示します
覚えておいてください:実際のビルドエラーには、実際の原因があります。原因を見つけて修正してください。キャッシュのせいにしないでください。
0. ⭐ 最優先ルール — ヘッダーファイルが見つからないエラーの解決
⚠️ 重要なルール — ヘッダーに関する「file not found」または「no such file or directory」エラーが発生した場合:
ルールの説明: ヘッダーファイルが見つからずコンパイルに失敗した場合は、必ず次の優先順位に従ってください。
- 推奨される解決策:エラーが発生した .cpp ファイルに不足しているヘッダーを追加します(最適化済みのヘッダーには追加しません)。
- 最後の手段:ユーザーの確認を得た場合に限り、最適化済みヘッダーの変更を検討します。
このルールが必要な理由:
- ビルド時間を短縮するため、ヘッダー最適化では意図的に依存関係を削除しています。
- 削除された依存関係を無条件に戻すと、最適化の取り組みが損なわれます。
- ヘッダーから削除された必要なインクルードは、.cpp ファイルに追加するのが適切です。
- インターフェイス(ヘッダー)と実装(.cpp)の分離を保てます。
最適化済みヘッダーの確認方法:
ヘッダーの変更を提案する前に、そのヘッダーファイルが最適化中の状態にあるか確認してください。
# Check 1: Staged changes (暂存状态)
git status --porcelain | grep "^M.*\.h$"
# Check 2: Modified but not staged (修改状态)
git status --porcelain | grep "^ M.*\.h$"
# Check 3: Recent commit (最近一笔提交)
git log -1 --name-only --pretty=format:"" | grep "\.h$"
# Combined check - all three states:
git status --porcelain | grep "\.h$" # Staged or modified
git diff HEAD~1 --name-only | grep "\.h$" # Changed in last commit
ヘッダーが次のいずれかの状態にある場合:
- ✅ 推奨:エラーが発生した .cpp ファイルに不足している #include を追加します。
- ❌ 禁止:最適化済みヘッダーに依存関係を戻すことを提案しないでください。
- ⚠️ 最後の手段:ヘッダーの変更がどうしても必要な場合は、必ず先にユーザーに確認してください。
.cpp ファイルに追加する場合(推奨):
// ✅ CORRECT - Add to the .cpp file with the compilation error
// frameworks/core/components_ng/pattern/search/search_gesture_event_hub.cpp
#include "core/events/click_event.h" // Add missing header here
// ❌ WRONG - Do NOT add back to optimized headers
// frameworks/core/components_ng/event/click_event.h
// #include "core/pipeline/base/element.h" // Don't restore deleted dependencies
ユーザー確認用テンプレート(ヘッダーの変更が必要な可能性がある場合):
⚠️ 检测到 [header_name.h] 可能正在被优化(状态:暂存/修改/最近提交)
编译错误:[error_message]
缺失头文件:[missing_header.h]
错误位置:[error_file.cpp:line]
建议方案:
✅ 方案1(推荐):在 [error_file.cpp] 中添加 #include "[missing_header.h]"
- 保持头文件优化成果
- 不增加头文件依赖
- 符合最佳实践
⚠️ 方案2(需确认):在 [header_name.h] 中添加回 #include "[missing_header.h]"
- 可能影响编译性能优化
- 需要用户确认必要性
请确认是否使用方案1,或说明为何需要方案2?
最適化済みヘッダーの例(確認なしに変更しないでください):
animation_utils.h— アニメーションユーティリティ関数click_event.h— クリックイベント処理- ステージ済み/変更済みの状態にあるヘッダー、または最近のコミットに含まれるヘッダー
確認ワークフロー:
ステップ 1:「file not found」エラーが発生した場合
# Extract the missing header path from error
# Example: error: 'core/pipeline/base/element.h' file not found
ステップ 2:参照元のファイルが最適化済みヘッダーか確認します。
# Find where this error occurs
grep -r "#include.*element.h" frameworks/core/components_ng/event/click_event.h
# Check if click_event.h is being optimized
git status --porcelain | grep "click_event.h"
git log -1 --name-only | grep "click_event.h"
ステップ 3:適切な解決策を適用します。
- ヘッダーが最適化済みの場合 → .cpp ファイルに追加します。
- ヘッダーが最適化済みでない場合 → ヘッダーに追加できます。
- 判断できない場合 → 両方の選択肢を示してユーザーに確認します。
確認手順:
- git status でステージ済み/変更済みのヘッダーを確認します。
- 直近のコミットでヘッダーに変更があったか確認します。
- コンパイルエラーが発生した .cpp ファイルを特定します。
- 不足している #include をその .cpp ファイルに追加します(推奨)。
- 修正によってエラーが解消されたことを確認します。
- ヘッダーの変更が必要だった場合は、その理由を記録します。
重要な原則:
- ✅ ヘッダー最適化の成果を維持します。
- ✅ 不足しているインクルードは .cpp の実装ファイルに追加します。
- ✅ ヘッダーの依存関係を適切に保ちます。
- ✅ ヘッダーの変更を提案する前に git status を確認します。
- ❌ 最適化の取り組みを無条件に元に戻さないでください。
- ❌ 最適化の状態を確認せずに、削除された依存関係をヘッダーに戻すことを提案しないでください。
- ❌ ステージ済み/変更済み/最近のコミットに含まれるヘッダーを、ユーザーの確認なしに変更しないでください。
関連スキル:
- header-optimization:ヘッダー最適化のパターンを理解するため
- compile-analysis:ヘッダーの依存関係を分析するため
- Case 6:RefPtr メンバーの前方宣言の最適化
関連ケース:
- ヘッダー最適化作業(header-optimization スキルを参照)
- 前方宣言のパターン(Case 6:RefPtr メンバーの最適化)
- コンパイルエラーの分析(Pattern 2:不完全型エラー)
1. ⭐ 最優先ルール ― 型の名前空間の混同(PixelMap など)
⚠️ 重大なルール ― 一般的なクラス名で型の不一致エラーが発生した場合:
ルールの説明: エラーメッセージに一般的なクラス名(PixelMap など)の型の不一致が表示された場合は、コードの場所に適した名前空間の型を使用しているか、必ず確認してください。
よくある落とし穴 ― PixelMap の名前空間の混同:
ACE Engine フレームワーク(frameworks/core/components_ng/ など)では:
- ✅ 正しい:
OHOS::Ace::PixelMap(base/image/pixel_map.hのラッパークラス) - ❌ 誤り:
OHOS::Media::PixelMap(基盤となるメディアクラス)
この問題が発生する理由:
Ace::PixelMapはMedia::PixelMapのラッパーです- ACE Engine フレームワークでの使用を想定して設計されています
- 場所:
base/image/pixel_map.h - IDE の自動補完で、誤って
Media::PixelMapが提案される場合があります
解決方法:
-
エラーの発生箇所を特定する:
- ファイルが
frameworks/ディレクトリ内にあるか確認します - フレームワークのコードパターンを使用しているか確認します
- ファイルが
-
正しい型を使用する:
// ✅ CORRECT for frameworks/ #include "base/image/pixel_map.h" RefPtr<PixelMap> pixelMap_; // OHOS::Ace::PixelMap // ❌ WRONG in frameworks/ #include "native_image/imageinfo.h" // Don't include media headers RefPtr<Media::PixelMap> pixelMap_; // Wrong namespace -
すべての該当箇所を修正する:
# Find wrong usage grep -rn "Media::PixelMap" frameworks/core/components_ng/ # Replace with correct type # Media::PixelMap → PixelMap (or OHOS::Ace::PixelMap)
このルールを適用する場合:
- PixelMap などの一般的な名前で型の不一致エラーが発生した場合
- 同じ型名に対して異なる名前空間がエラーに表示された場合
- ファイルの場所が
frameworks/内(interfaces/ではない)の場合 - 戻り値の型に関する仮想関数のオーバーライドエラーが発生した場合
禁止事項:
- ❌ IDE が提案したという理由だけで、
Media::PixelMapが正しいと判断しないでください - ❌ 名前空間の問題を修正するために
using Media::PixelMap;を追加しないでください - ❌ 派生クラスに合わせて基底クラスを変更しないでください(派生クラスを変更してください)
- ❌ フレームワークのコードでメディア API の型を使用するよう提案しないでください
確認事項:
- インクルードを確認します:
#include "base/image/pixel_map.h"が存在すること - 使用箇所を確認します:
RefPtr<PixelMap>またはRefPtr<OHOS::Ace::PixelMap>を使用していること - フレームワークのコードに
Media::PixelMapの参照がないこと - 仮想関数の戻り値の型が基底クラスと一致していること
関連するパターン:
- 同様の問題は、ほかのラッパー型でも発生する場合があります
- メディア型を使用する前に、Ace のラッパーがあるか必ず確認してください
- フレームワークでは Ace のラッパーを使用し、インターフェースではメディア型を使用する場合があります
2. ビルドシステムのアーキテクチャ
- libace.z.so は ace_core_ng ライブラリのみをリンクします
- コンポーネント固有のライブラリは、最終成果物にリンクされません
- libace.z.so で使用する新しい .cpp ファイルは、ace_core_ng_source_set に含める必要があります
2. シンボルのエクスポートルール ⭐
⚠️ 重要 — 非テンプレートクラスおよび非テンプレートメソッドのルール:
非テンプレートクラスおよび非テンプレートメソッドで「undefined symbol」のリンカーエラーが発生した場合は、次のルールを順番に適用してください。
ルール 1:クラスに ACE_FORCE_EXPORT があるか確認する
まず、クラス定義に ACE_FORCE_EXPORT があるか確認します。
// Case A: Class WITHOUT ACE_FORCE_EXPORT
class PaddingPropertyF { // ← No export on class
float Width() const;
};
// Case B: Class WITH ACE_FORCE_EXPORT
class ACE_FORCE_EXPORT PaddingPropertyF { // ← Export on entire class
float Width() const;
};
ルール 2:クラスのエクスポート状況に応じてエクスポートを適用する
ケース A — クラスに ACE_FORCE_EXPORT がない場合:
- ✅ ヘッダー内のメソッド宣言に
ACE_FORCE_EXPORTを追加します(推奨) - ❌ .cpp 内のメソッド定義には追加しないでください
- 理由:エクスポートの制御を宣言レベルに保ち、きめ細かなエクスポート制御を可能にします
// ✅ CORRECT - Add to header declaration
// interfaces/inner_api/ace_kit/include/ui/properties/ng/measure_property.h
class PaddingPropertyF {
ACE_FORCE_EXPORT float Width() const; // ← Add here
ACE_FORCE_EXPORT float Height() const; // ← Add here
};
// frameworks/core/components_ng/property/measure_property.cpp
// NO ACE_FORCE_EXPORT here
float PaddingPropertyF::Width() const { /* implementation */ }
ケース B — クラスに ACE_FORCE_EXPORT がある場合:
- ✅ 追加のエクスポートは不要です。クラスのエクスポートによってすべてのメソッドが対象になります
- ❌ 個々のメソッドに
ACE_FORCE_EXPORTを追加しないでください - 理由:クラス全体がすでにエクスポートされています
// ✅ CORRECT - No individual method exports needed
class ACE_FORCE_EXPORT PaddingPropertyF { // ← Entire class exported
float Width() const; // Automatically exported
float Height() const; // Automatically exported
};
ルール 3:きめ細かなシンボル名を使って libace.map に追加する ⭐
⚠️ 重要:必ず、最初の選択肢としてきめ細かなシンボルのエクスポートを優先してください
ルール 1 とルール 2 を適用した後、シンボルを build/libace.map に追加します。
// build/libace.map
{
global:
// ✅ FINE-GRAINED - Export specific methods (PREFERRED - ALWAYS TRY THIS FIRST)
OHOS::Ace::PaddingPropertyF::Width*;
OHOS::Ace::PaddingPropertyF::Height*;
// ✅ FINE-GRAINED CONSTRUCTORS - Match all constructor overloads
OHOS::Ace::VelocityTracker::VelocityTracker*; // Matches both constructors
// ⚠️ ACCEPTABLE - Export entire class (ONLY if fine-grained not feasible)
OHOS::Ace::PaddingPropertyF::*;
};
きめ細かなエクスポートパターンの優先順位(優先度の高い順):
-
メソッドレベル(最も推奨):
OHOS::Ace::ClassName::MethodName*; # Single method -
コンストラクターレベル(コンストラクター):
OHOS::Ace::ClassName::ClassName*; # All constructor overloads -
クラスレベル(最後の手段):
OHOS::Ace::ClassName::*; # All class members (use sparingly)
きめ細かなエクスポートを使う理由 ⭐
- ⭐ エクスポートされるシンボルの範囲を最小限にできます — 必要なものだけを対象にします
- ⭐ API の境界が明確になります — 公開インターフェースを明示できます
- ⭐ シンボルの競合を減らせます — 対象範囲が狭くなり、競合が減ります
- ⭐ 依存関係を追跡しやすくなります — モジュール間の使用状況を容易に把握できます
- ⭐ セキュリティが向上します — エクスポートされる攻撃対象領域を制限できます
- ⭐ 保守が容易になります — API 契約に含まれるシンボルが明確になります
判断ツリー:
Linker Error: undefined symbol ClassName::MethodName
↓
Is ClassName a non-template class?
YES → Is class declaration has ACE_FORCE_EXPORT?
YES → ✅ Class already exported
→ Add ClassName::* to libace.map
→ DONE (no individual method exports needed)
NO → ✅ Add ACE_FORCE_EXPORT to method declaration in .h
→ Add ClassName::MethodName* to libace.map
→ DONE
NO → Use template export rules (see below)
比較:非テンプレートとテンプレート
| 項目 | 非テンプレートクラス/メソッド | テンプレートクラス/メソッド |
|---|---|---|
| エクスポートの場所 | .h 内のメソッド宣言(ルール 2) | .h 内の宣言と .cpp 内の定義の両方 |
| クラスのエクスポート | 指定されていれば、すべてのメソッドが対象になります | 個々のメソッドにもエクスポートが必要です |
| libace.map | きめ細かく指定:ClassName::MethodName*; | ワイルドカード:ClassName*; または TemplateFunction*; |
| 参照先 | このセクション(ルール 2) | パターン 1、シナリオ 3 および 5 |
⚠️ テンプレート専用のルール(非テンプレートには適用しないでください):
テンプレートクラスおよびテンプレート関数のみには、別のルールを適用します。
テンプレート関数(パターン 1、シナリオ 3):
- .h 内の宣言と .cpp 内の実装の両方に
ACE_FORCE_EXPORTを追加します - .cpp 内に明示的なインスタンス化を追加します
- libace.map ではワイルドカードを使います:
void?ClassName::FunctionName*;
テンプレートクラスのメソッド(パターン 1、シナリオ 5):
- .cpp 内の各メソッド実装に
ACE_FORCE_EXPORTを追加します - クラスのエクスポートだけでは不十分です
- libace.map ではワイルドカードを使います:
ClassName*;
ルールを混同しないでください:
- ❌ 非テンプレートクラスにテンプレート用のルールを適用しないでください
- ❌ テンプレートクラスに非テンプレート用のルールを適用しないでください
3. ヘッダーの最適化
- ヘッダーのみの定数には
inline constexprを使います - ヘッダーと .cpp の両方で定義しないでください
- 前方宣言によって依存関係を減らせますが、使用箇所で完全な型が見えるようにしてください
⭐ 重要な原則 — モジュール間で使用されるシンボルのエクスポート
他の動的ライブラリから使用されるメソッドで「undefined symbol」のリンカーエラーが発生した場合:
⚠️ 重要:非テンプレートとテンプレートを区別する
エクスポートルールを適用する前に、まず種類を確認してください:
Linker Error: undefined symbol
↓
Is it a non-template class method?
→ YES: Use Non-Template Rules (below)
→ NO: Use Template Rules (Pattern 1, Scenario 3 & 5)
非テンプレートクラスのメソッド:宣言時にエクスポート ⭐
✅ 非テンプレートにはこちらを推奨 — ヘッダー(.h)のメソッド宣言に ACE_FORCE_EXPORT を追加します:
// interfaces/inner_api/ace_kit/include/ui/properties/ng/measure_property.h
class PaddingPropertyF {
ACE_FORCE_EXPORT float Width() const; // ← Add here (PREFERRED)
ACE_FORCE_EXPORT float Height() const; // ← Add here (PREFERRED)
};
// frameworks/core/components_ng/property/measure_property.cpp
// NO ACE_FORCE_EXPORT here for non-template methods
float PaddingPropertyF::Width() const
{
return left.value_or(0.0f) + right.value_or(0.0f);
}
非テンプレートでは、なぜこの方法を使うのですか?
- きめ細かなエクスポート制御 — 必要なメソッドのみをエクスポートします
- 明確な API 境界 — 宣言を見れば、何がエクスポートされるか分かります
- libace.map のきめ細かなルールに対応 — 特定のシンボルをエクスポートします
- クラスのエクスポートの仕組みと一貫性がある — クラスがエクスポートされている場合、個別にエクスポートする必要はありません
⚠️ 重要:まずクラスのエクスポート状態を確認してください
// Case A: Class WITHOUT export → Add to individual method declarations
class PaddingPropertyF {
ACE_FORCE_EXPORT float Width() const; // ← Required
};
// Case B: Class WITH export → No individual exports needed
class ACE_FORCE_EXPORT PaddingPropertyF { // ← Entire class exported
float Width() const; // Automatically exported, no ACE_FORCE_EXPORT needed
};
❌ 非テンプレートでは、以下を行わないでください:
- ❌ テンプレートの場合を除き、.cpp のメソッド定義に
ACE_FORCE_EXPORTを追加しないでください - ❌ クラスにすでに
ACE_FORCE_EXPORTが指定されている場合、個々のメソッドをエクスポートしないでください - ❌ 非テンプレートコードにテンプレートのエクスポートルールを使用しないでください
テンプレートのみ:適用されるルールが異なります ⚠️
テンプレートクラスとテンプレート関数には、異なるルールを使用します(パターン 1、シナリオ 3 および 5 を参照):
| ルールの種類 | 非テンプレート | テンプレート |
|---|---|---|
| エクスポートする場所 | .h のメソッド宣言 | .h の宣言と .cpp の定義の両方 |
| クラスのエクスポートで十分か? | はい(すべてのメソッドが対象) | いいえ(個々のメソッドをエクスポートする必要があります) |
| libace.map のパターン | きめ細かい指定:ClassName::MethodName*; | ワイルドカード:ClassName*; |
| 参照先 | このセクション | パターン 1、シナリオ 3 および 5 |
各方法を使う場面
| シナリオ | 推奨する場所 | 例 |
|---|---|---|
| 非テンプレートクラスのメソッド | .h のメソッド宣言 | PaddingPropertyF::Width() |
| テンプレートクラスのメソッド | .h の宣言と .cpp の実装の両方 | LayoutConstraintT<T>::UpdateMaxSizeWithCheck() |
| テンプレート関数 | .h の宣言と .cpp の実装の両方 | StringUtils::StringSplitter<T> |
| フリー関数(非テンプレート) | .h のメソッド宣言 | StringUtils::TransformStrCase<std::string> |
| クラスがすでにエクスポートされている場合 | 不要 | すべてのメソッドが自動的にエクスポートされます |
実際に成功したケース
-
非テンプレートメソッド(PaddingPropertyF):
- .h のメソッド宣言に
ACE_FORCE_EXPORTを追加 - .cpp ファイルは変更しない
- きめ細かな libace.map エントリー:
OHOS::Ace::PaddingPropertyF::Width*;
- .h のメソッド宣言に
-
テンプレートクラスのメソッド(LayoutConstraintT):
- .cpp の各メソッド実装に
ACE_FORCE_EXPORTを追加 - テンプレートでは、クラスのエクスポートだけでは不十分
- libace.map のワイルドカードパターン:
OHOS::Ace::NG::LayoutConstraintT*;
- .cpp の各メソッド実装に
-
テンプレート関数(StringUtils):
- .h の宣言と .cpp の実装の両方に
ACE_FORCE_EXPORTを追加 - .cpp で明示的にインスタンス化
- libace.map のワイルドカードパターン:
void?OHOS::Ace::StringUtils::StringSplitter*;
- .h の宣言と .cpp の実装の両方に
覚えておいてください:他のモジュールで必要なシンボルに対するリンカーエラーを修正する場合、ヘッダーを変更する前に、まず .cpp の実装を変更する方法を試してください。
4. テンプレートの明示的なインスタンス化 ⭐
明示的なインスタンス化を行うテンプレートクラスの場合:
問題:PaddingPropertyT<CalcLength> のシンボルはエクスポートされませんが、PaddingPropertyT<Dimension> はエクスポートされます
根本原因:型を明示的なインスタンス化のテンプレート引数として使用する場合、その型自体にエクスポート属性が必要です
重要な原則:
Template Type Visibility (CalcLength)
↓
Template Specialization (PaddingPropertyT<CalcLength>)
↓
Symbol Export Success/Failure
CalcLengthにエクスポートがない場合 → テンプレートの特殊化は内部可視性を継承しますCalcLengthにACE_FORCE_EXPORTがある場合 → テンプレートの特殊化をエクスポートできます
解決策:
// ❌ BEFORE - Type missing export (fails when used as template parameter)
class CalcLength {
std::string ToString() const { /* inline */ }
};
// ✅ AFTER - Add type-level export (required for template parameters)
class ACE_FORCE_EXPORT CalcLength { // ← Required when used as template type
std::string ToString() const { /* inline - keep in header */ }
};
重要なルール:
- テンプレート引数として使用する型にはエクスポートが必須です(型にインラインメソッドがある場合)
- インラインメソッドを .cpp に移動しないでください - ヘッダーの最適化を維持してください
- テンプレートメソッドには、引き続き .cpp で
ACE_FORCE_EXPORTが必要です - .cpp で明示的にインスタンス化する必要があります
型レベルのエクスポートが必要な場合:
- ✅ 型にインラインメソッド(ToString、operator== など)がある
- ✅ 型が明示的なインスタンス化のテンプレート引数として使用されている
- ✅ テンプレートのインスタンス化シンボルを他のモジュールにエクスポートする必要がある
参考:詳細な事例については references/template-instantiation-type-export.md を参照してください
⚠️ 重要 - ヘッダーの最適化:
- StringUtils のリファクタリング(コミット e0fd3724d21)では、ヘッダーから .cpp に約 650 行を移動しました
- これはビルド時間の改善を目的とした戦略的な作業です
- インラインメソッドを .cpp に移動して元に戻さないでください
- テンプレート引数のエクスポート問題は、型レベルのエクスポートを追加して解決してください
検証手順
ユーザーが推奨される修正を適用した後、次の検証コマンドを提案してください:
# 1. Navigate to OpenHarmony root directory and rebuild
cd <openharmony_root>
./build.sh --product-name rk3568 --build-target ace_engine --ccache
# 2. Check for link errors in out/<product>/build.log
grep "ld.lld: error:" out/rk3568/build.log | wc -l
# 3. Verify symbol export (if applicable)
nm -D out/rk3568/arkui/ace_engine/libace.z.so | grep SymbolName
# 4. Extract and check new errors from out/<product>/build.log
foundation/arkui/ace_engine/.claude/skills/build-error-analyzer/script/extract_last_error.sh out/rk3568/build.log
# 5. Read the extracted errors:
cat out/rk3568/last_error.log
# For SDK builds (⚠️ use out/sdk/ directory):
# 1. Build SDK: ./build.sh --product-name ohos-sdk --ccache
# 2. Check errors: grep "error:" out/sdk/build.log | tail -50
# 3. Extract errors: foundation/arkui/ace_engine/.claude/skills/build-error-analyzer/script/extract_last_error.sh out/sdk/build.log
# 4. Read errors: cat out/sdk/last_error.log
注:これらはユーザーが手動で実行するための提案です。ビルドコマンドを自動的に実行しないでください。
新しいケースの追加
新しいビルドエラーを解決する際は:
- エラーを
examples/<case-name>.mdに記録する - 次の項目を含める:
- エラーシグネチャ
- 根本原因
- 解決手順
- 変更したファイル
- 検証コマンド
- 新しいケースを参照するよう、この SKILL.md を更新する
- 「エラーパターンと解決策」セクションにパターンを追加する
使用例
例 1:ビルド成功(エラーなし)
User: "帮我分析一下构建错误"
Assistant: "I'll analyze the build errors from the OpenHarmony output directory.
⚠️ FIRST, I'll extract the latest errors from build.log:
[STEP 1 - NAVIGATE TO ROOT]
cd <openharmony_root>
[STEP 2 - EXTRACTION - MANDATORY]
foundation/arkui/ace_engine/.claude/skills/build-error-analyzer/script/extract_last_error.sh out/rk3568/build.log
Output:
Extracted latest error block (24 bytes) from out/rk3568/build.log
→ Saved to out/rk3568/last_error.log
[STEP 3 - READ EXTRACTED ERRORS]
Reading out/rk3568/last_error.log...
Content:
build success, no error
[STEP 4 - CHECK FOR SUCCESS]
✅ Build is successful! No errors found.
## ✅ 构建成功
最新的构建已经成功完成,没有发现任何错误。
**构建状态**: 成功
**错误信息**: 无
建议: 您可以继续进行开发或测试工作。
[STOP - DO NOT read other log files like error.log]"
例 2:エラーのあるビルド
User: "帮我分析一下构建错误"
Assistant: "I'll analyze the build errors from the OpenHarmony output directory.
⚠️ FIRST, I'll extract the latest errors from build.log:
[STEP 1 - NAVIGATE TO ROOT]
cd <openharmony_root>
[STEP 2 - EXTRACTION - MANDATORY]
foundation/arkui/ace_engine/.claude/skills/build-error-analyzer/script/extract_last_error.sh out/rk3568/build.log
Output:
Extracted latest error block (12345 bytes) from out/rk3568/build.log
→ Saved to out/rk3568/last_error.log
[STEP 3 - READ EXTRACTED ERRORS]
Reading out/rk3568/last_error.log...
[STEP 4 - CATEGORIZE]
Identified as: compilation error - undeclared identifier
[STEP 5 - MATCH PATTERNS]
Checking references/ directory for similar patterns...
Found match: references/undefined-symbol-missing-cpp.md
[STEP 6 - PROVIDE RECOMMENDATIONS]
Providing detailed fix recommendations (NOT modifying files)
## 构建错误分析
### 错误类型
编译错误 - 未声明的标识符
### 错误位置
frameworks/core/event/touch_event.cpp:25
### 根本原因
函数被删除但调用点未移除:TouchPoint::CovertId() 已删除,但头文件中仍有声明且多处调用
### 修复建议
**选项1: 恢复函数实现** (推荐)
在 frameworks/core/event/touch_event.cpp 中恢复函数:
**当前代码**: (函数已删除)
**应添加**:
```cpp
void TouchPoint::CovertId()
{
if (sourceTool == SourceTool::PEN) {
originalId = TOUCH_TOOL_BASE_ID + static_cast<int32_t>(sourceTool);
id = id + originalId;
}
}
オプション2:関数を完全に削除する
次のファイルを変更する必要があります:
- ヘッダーの宣言を削除する:interfaces/inner_api/ace_kit/include/ui/event/touch_event.h:59
- 呼び出し箇所を削除する:adapter/ohos/entrance/mmi_event_convertor.cpp:240
- テストケースを削除する:test/unittest/core/event/touch_event_test_ng.cpp:668
検証手順
- 再コンパイルする:./build.sh --product-name rk3568 --build-target ace_engine
- エラーを再抽出する:foundation/arkui/ace_engine/.claude/skills/build-error-analyzer/script/extract_last_error.sh out/rk3568/build.log
- 結果を確認する:cat out/rk3568/last_error.log
[NOTE: 推奨事項のみを提示し、ファイルは変更しません]
**Key Points**:
- ⚠️ **ALWAYS navigate to root first** → `cd <openharmony_root>` before running extraction script
- ⚠️ **ALWAYS extract first** → use script on `out/<product>/build.log` with correct relative path from root
- ✅ **THEN read** → analyze only from `out/<product>/last_error.log`
- ❌ **NEVER read** → `build.log` directly or other log files
- ✅ **Check for success** → if `last_error.log` shows "build success", STOP and report
- ❌ **NEVER read other logs** → DO NOT read `error.log`, `build_output*.log`, `error.*.log`, etc.
- ✅ **Use correct script path** → `foundation/arkui/ace_engine/.claude/skills/build-error-analyzer/script/` (from OpenHarmony root)
- ✅ **Provide recommendations ONLY** → do NOT automatically modify code
## Version History
- **0.14.0** (2026-02-10): Non-Template Symbol Export Rules
- ⚠️ **重要修改**:区分非模板类和模板类的符号导出规则
- 📋 **非模板类规则**:优先在方法声明处(.h 文件)增加 ACE_FORCE_EXPORT
- 🔍 **类导出检查**:如果 class 已有 ACE_FORCE_EXPORT,则无需额外导出方法
- 🎯 **精细化导出**:libace.map 中优先使用细粒度符号名(ClassName::MethodName*)
- 📊 **决策树**:添加非模板 vs 模板的决策流程图
- 🔧 **对比表格**:非模板与模板导出规则的清晰对比
- ✅ **更新位置**:
- Best Practices 部分:"### 2. Symbol Export Rules ⭐"
- Key Principles 部分:"## ⭐ Key Principles - Symbol Export for Cross-Module Usage"
- Pattern 1 Scenario 2:更新为基于类导出状态的决策流程
- 🚫 **禁止事项**:非模板方法不要在 .cpp 中添加 ACE_FORCE_EXPORT
- 💡 **核心理念**:非模板支持精细化导出控制,模板需要宽泛通配符
- **0.13.0** (2026-02-10): CRITICAL RULE - Never Clear LTO Cache
- ⚠️ **关键修改**:绝对禁止建议清理 LTO cache 或删除构建目录
- 📋 在 Behavior Guidelines 部分添加 "CRITICAL - NEVER SUGGEST CLEARING LTO CACHE" 子部分
- 🚨 在 Best Practices 部分添加全新的 "CRITICAL RULE - NEVER Clear LTO Cache" 高优先级规则
- 💡 强调 LTO cache 问题极其罕见(99.9% 的情况都不是缓存问题)
- ⏱️ 说明清理缓存会浪费大量时间(完整重建需要数小时,增量编译只需数分钟)
- 🎯 明确要求必须分析真实错误并修复根本原因,而不是怪罪缓存
- 📝 加强 Pattern 1 Common mistakes 部分的相关说明
- ⛔ 仅在极端罕见情况下(用户明确修改了编译器标志等)才考虑缓存问题
- ✅ 即使在那些情况下,也必须先询问用户并记录具体原因
- **0.12.0** (2026-02-10): Case 11 - std::function<RefPtr<T>> Forward Declaration
- ✨ 新增 Case 11:std::function<RefPtr<T>> 前向声明成功案例
- 📝 新增案例分析文档:`case-std-function-refptr-forward-declaration.md`
- 🎯 核心观点:using 别名不立即实例化模板,RefPtr<T> 大小固定
- 💡 关键发现:模板实例化延迟到 .cpp 实际使用时
- 📊 对比表格:Case 6/8/11 的 RefPtr<T> 前向声明使用场景
- 🔧 纠正错误理解:std::function 不需要完整类型定义
- ✅ 实战案例:OpenHarmony gesture_event_hub.h 优化成功应用
- 🛡️ 验证原则:RefPtr<> 本质是指针包装器,前向声明可行
- **0.11.0** (2026-02-09): High Priority Rule - Header Optimization Protection
- ⭐ 新增高优先级规则:处理头文件缺失错误时的优先级顺序
- 🔍 新增优化检测方法:通过 git status 检测暂存/修改/最近提交状态
- ✅ 优先方案:在出错的 .cpp 文件中添加缺失头文件(推荐)
- ⚠️ 最后手段:仅在用户确认后才修改已优化的头文件
- 📝 新增用户确认模板:清晰展示两个方案供用户选择
- 🔧 新增检测工作流:三步检测并应用正确的解决方案
- 🛡️ 保护头文件优化成果:避免盲目回退已删除的依赖
- 🔗 关联 header-optimization 和 compile-analysis skills
- **0.10.0** (2026-02-06): Case 8 Advanced Scenario - Struct with Own .cpp File
- ✨ 新增 Case 8 进阶场景:当辅助方法模式不够时的完整解决方案
- 📝 在 `forward-declaration-struct-helper-method.md` 中添加 Advanced Scenario
- 🎯 应用场景:结构体有 .cpp 文件,被 std::vector 使用,需要保持聚合初始化
- 💡 关键技术:升级为完整的前向声明优化(类似 Case 6),同时保留辅助方法
- 🔧 决策树:指导何时使用标准方案 vs 完整方案
- 📊 对比表格:标准方案(无 .cpp)vs Advanced Scenario(有 .cpp)
- ⚠️ 渐进式升级:从辅助方法模式开始,遇到 vector 拷贝问题时升级
- 🔗 关联 Case 6 和 Case 7:完整的 RefPtr 成员优化知识体系
- **0.9.0** (2026-02-04): LTO Virtual Thunk Export Solution
- ✨ 新增 LTO Virtual Thunk 导出案例分析:`lto-virtual-thunk-libace-map-export.md`
- 📝 新增 Case 9:基类虚函数 + LTO 的 libace.map 导出解决方案
- 🎯 核心观点:保持前向声明优化,不要回退到 inline 定义
- 💡 关键技术:在 libace.map 中同时导出 `ClassName::*;` 和 `virtual?thunk?to::ClassName::*;`
- 🔧 LTO 工作原理:链接时创建 virtual thunk 调整 this 指针
- 📊 符号导出模式:通配符 `*` 匹配所有方法和 virtual thunk
- ⚠️ 常见错误:只导出类符号而忘记导出 virtual thunk
- **0.8.0** (2026-02-04): Struct RefPtr Member Optimization
- ✨ 新增结构体智能指针成员优化案例分析:`forward-declaration-struct-helper-method.md`
- 📝 新增 Case 8:纯数据结构中 RefPtr 成员的辅助方法模式
- 🎯 与 Case 6 的区别:Case 6 适用于有自己 .cpp 的类,Case 8 适用于纯数据结构
- 💡 关键技术:使用辅助方法封装完整类型访问,保持头文件前向声明优化
- 🔧 完全限定名 `::OHOS::Media::PixelMap` 避免命名空间混淆
- 📊 编译性能优化:减少头文件依赖,降低重编译范围
- **0.7.0** (2026-02-02): Test Linking Error Support
- ✨ 新增测试链接错误案例分析:`test-missing-source-files.md`
- 📝 新增 Case 7:测试中缺失源文件的通用解决方案
- 🎯 泛化案例场景,不仅限于 LogWrapper,涵盖所有缺失源文件情况
- 📋 列出常见缺失源文件:log_wrapper.cpp、string_utils.cpp、layout_constraint.cpp 等
- ⚠️ 强调只添加源文件,不修改 cflags/configs/defines
- 🌳 添加决策树帮助判断正确的解决方案
- **0.6.0** (2025-02-02): 新增 SDK 编译错误分析支持
- ✨ 添加 SDK 错误触发关键词:"分析SDK的编译错误"、"analyze SDK build errors"、"check SDK errors"
- ⚠️ 特别说明:SDK 日志路径为 `out/sdk/build.log` 和 `out/sdk/last_error.log`(特殊目录)
- 📝 更新所有路径示例,标注 SDK 的特殊目录结构
- 🔧 优化错误提取命令,支持 SDK 路径
- **0.5.0** (2026-01-27): 模板类方法导出支持
- **0.4.0** (2026-01-27): 模板实例化类型导出支持
- **0.3.0** (2026-01-27): 符号导出最佳实践
- **0.2.0** (2026-01-27): 添加重定义错误案例
- **0.1.0** (2026-01-27): 初始版本,包含未定义符号案例研究
## Related Skills
- **openharmony-build**: For building OpenHarmony codebase
- **compile-analysis**: For analyzing compilation performance and dependencies