Build Error Analyzer Skill
This skill specializes in analyzing OpenHarmony build errors from last_error.log and providing fix recommendations only (no automatic code modifications) based on error patterns and historical case studies.
IMPORTANT: This skill provides analysis and recommendations ONLY. It does NOT automatically modify code files.
⚠️ CRITICAL WORKFLOW (MUST FOLLOW)
YOU MUST ALWAYS FOLLOW THIS EXACT SEQUENCE:
- FIRST: Extract errors from
out/<product>/build.log→ generatesout/<product>/last_error.log - THEN: Read and analyze ONLY from
out/<product>/last_error.log - NEVER: Read directly from
out/<product>/build.logor any other log files (error.log, build_output*.log, etc.) - SUCCESS CASE: If
last_error.logcontains "build success" or "no error", STOP and report success
Why?:
build.logcontains the entire build history (thousands of lines)last_error.logcontains ONLY the most recent error block (extracted by script)- Reading
build.logdirectly will give you STALE or IRRELEVANT errors - The extraction script ensures you always analyze the LATEST errors
- Other log files (error.log, build_output*.log) may contain outdated errors from previous builds
Before reading ANY errors, ALWAYS run:
# ⚠️ 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
⚠️ CRITICAL - CHECK FOR SUCCESS FIRST:
After reading last_error.log, if you see:
build success, no errorno error- Or similar success messages
IMMEDIATELY STOP and report:
## ✅ 构建成功
最新的构建已经成功完成,没有发现任何错误。
**构建状态**: 成功
**错误信息**: 无
建议: 您可以继续进行开发或测试工作。
DO NOT:
- ❌ Continue searching for errors in other log files
- ❌ Read error.log
- ❌ Read build_output*.log files
- ❌ Look for historical errors
Important Path Information
Always use these paths:
- Extraction Script:
foundation/arkui/ace_engine/.claude/skills/build-error-analyzer/script/extract_last_error.sh(relative to OpenHarmony root) - Source Build Log:
out/<product>/build.log(e.g.,out/rk3568/build.log) - DO NOT READ DIRECTLY - Target Error Log:
out/<product>/last_error.log(auto-generated in same directory as build.log) - READ ONLY THIS FILE
⚠️ SPECIAL CASE - SDK Build Paths:
- SDK Build Log:
out/sdk/build.log(NOTout/ohos-sdk/build.log) - SDK Error Log:
out/sdk/last_error.log(NOTout/ohos-sdk/last_error.log) - SDK Product Exception: SDK uses
out/sdk/directory instead ofout/ohos-sdk/ - Trigger Keywords: "分析SDK的编译错误", "analyze SDK build errors", "check SDK errors"
Workflow:
- ✅ Extract errors from
out/<product>/build.logusing the script → createsout/<product>/last_error.log - ✅ For SDK: Extract from
out/sdk/build.log→ createsout/sdk/last_error.log - ✅ Analyze errors from the extracted
last_error.logONLY - ✅ Provide fix recommendations ONLY - do NOT modify code
Common Mistakes to Avoid:
- ❌ Reading
build.logdirectly (will get stale/wrong errors) - ❌ Reading
error.log(contains outdated errors from previous builds) - ❌ Reading other log files (build_output*.log, error.*.log, etc.)
- ❌ Continuing to search for errors when
last_error.logshows "build success" - ❌ Analyzing errors without extraction step
- ❌ Running extraction script from wrong directory (MUST be from OpenHarmony root)
- ✅ ALWAYS cd to
<openharmony_root>first - ✅ ALWAYS extract → then read
last_error.logONLY - ✅ If
last_error.logshows success → STOP and report success
Behavior Guidelines
This skill provides ANALYSIS and RECOMMENDATIONS ONLY:
✅ DO:
- Extract errors from build logs
- Analyze and categorize errors
- Match errors against known patterns
- Provide detailed fix recommendations with file paths and line numbers
- Show before/after code examples
- Explain root causes
- Suggest verification steps
❌ DO NOT:
- Use Edit tool to modify code files
- Use Write tool to create or modify files
- Run build commands automatically
- Apply fixes without user confirmation
- Make any changes to the codebase
- Read from log files other than
last_error.log(error.log, build_output*.log, error.*.log, etc.) - Continue searching for errors when build is successful
⚠️ CRITICAL - NEVER SUGGEST CLEARING LTO CACHE:
- ❌ NEVER suggest clearing
thinlto-cachedirectory - ❌ NEVER suggest clearing
llvmcache-*directories - ❌ NEVER suggest deleting
out/objdirectory - ❌ NEVER suggest deleting entire
out/directory - ❌ NEVER blame LTO cache for build errors
- ❌ NEVER suggest "clean build" as first solution
Why: The user should review and apply fixes manually to maintain control over code changes and understand the modifications. LTO cache issues are extremely rare and clearing caches as first resort masks real problems, wastes rebuild time, and disrupts incremental compilation benefits.
Prerequisites
- ⚠️ CRITICAL: ALWAYS extract from
out/<product>/build.logFIRST before reading - Error log location: The skill reads errors ONLY from
out/<product>/last_error.log(same directory as build.log) - Error extraction: ALWAYS use the extraction script from
foundation/arkui/ace_engine/.claude/skills/build-error-analyzer/script/extract_last_error.sh(from OpenHarmony root) to generate/updatelast_error.logbefore reading - Source build.log: Errors are extracted ONLY from
out/<product>/build.login the OpenHarmony output directory - Codebase context: Can be executed from any OpenHarmony directory (ace_engine, root, etc.)
- ⚠️ SUCCESS CHECK: If
last_error.logcontains "build success" or "no error", STOP and report success - DO NOT read other log files likeerror.log,build_output*.log, etc. - EXCLUSIVE LOG SOURCE: ONLY read from
last_error.log- NEVER fromerror.log,build_output*.log,error.*.log, or any other log files
Analysis Workflow
Step 1: Extract Latest Error (REQUIRED - ALWAYS DO THIS FIRST)
⚠️ CRITICAL: This step is MANDATORY and must be done BEFORE reading any errors.
Why this is required:
build.logcontains the full build output (old and new errors mixed together)- Reading it directly will analyze STALE or IRRELEVANT errors
- The extraction script isolates ONLY the most recent error block
last_error.logis generated in the same directory asbuild.log
⚠️ IMPORTANT: Always navigate to OpenHarmony root first, then run the script:
# 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
The script will:
- Extract ONLY the latest error block from
out/<product>/build.log - Output to
out/<product>/last_error.log(same directory) - Show error block size and summary
- Overwrite previous
last_error.logif it exists
⚠️ DO NOT SKIP THIS STEP
- Even if
last_error.logexists, RE-EXTRACT to get the latest errors - DO NOT read
build.logdirectly - DO NOT read other log files (build_output*.log, error.log, error.*.log, etc.)
- ONLY analyze
last_error.logafter extraction
Step 2: Read and Categorize Errors
⚠️ CRITICAL: Only read last_error.log from out/<product>/ directory in the OpenHarmony output tree.
After extraction, the error log is located at:
out/<product>/last_error.log(same directory as build.log)
Read the extracted error 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
⚠️ CRITICAL - CHECK FOR SUCCESS FIRST:
Before any analysis, CHECK if the build succeeded:
If last_error.log contains:
build success, no errorno error- Or similar success messages
IMMEDIATELY STOP and report:
## ✅ 构建成功
最新的构建已经成功完成,没有发现任何错误。
**构建状态**: 成功
**错误信息**: 无
建议: 您可以继续进行开发或测试工作。
DO NOT continue to:
- ❌ Search for errors in error.log
- ❌ Search for errors in build_output*.log
- ❌ Search for errors in error.*.log files
- ❌ Look for historical errors
- ✅ STOP and report success
Only if there are actual errors, proceed to categorize them:
Error Categories:
- Compilation errors (CXX tasks) - Syntax, missing headers, incomplete types
- Linker errors (SOLINK/LINK tasks) - Undefined symbols, missing libraries
- Build system errors - GN/Ninja configuration issues
Step 3: Match Against Known Cases
Compare error patterns against historical cases in references/ directory and provide specific solutions.
Case matching priority:
- Match by error signature (e.g., "undefined symbol" + "referred by")
- Match by error context (e.g., "timepicker module" usage)
- Match by file pattern (e.g., BUILD.gn issues)
- Provide general guidance if no specific case matches
Step 4: Provide Fix Recommendations (NO Automatic Modifications)
CRITICAL: This step provides recommendations ONLY. Do NOT use Edit/Write tools to modify code.
Based on error type and matched cases, provide:
- Root cause analysis
- Specific file modifications needed (with exact line numbers and code snippets)
- Build system configuration changes (with exact GN file paths)
- Verification steps
Recommended Output Format:
## 构建错误分析
### 错误类型
[编译错误/链接错误/构建系统错误]
### 错误位置
[文件路径:行号]
### 错误信息
[完整的错误消息]
### 根本原因
[详细解释为什么会出现这个错误]
### 修复建议
**文件**: [完整文件路径:行号]
**当前代码**:
```cpp
[错误代码片段]
应修改为:
[正确代码片段]
修改说明: [解释为什么要这样修改]
验证步骤
- [第一步验证操作]
- [第二步验证操作]
- [如何确认修复成功]
参考
[相关的历史案例或文档链接]
**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:
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/
If implementation exists, check BUILD.gn:
grep -r "implementation_file.cpp" frameworks/*/BUILD.gnAdd .cpp to BUILD.gn:
- Find where other files in same directory are added
- Add the missing .cpp to the appropriate source_set
- Important: Reference files in same directory to find correct source_set
- DO NOT assume all files go to
ace_core_ng_source_set
Verify with other files:
# 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
Scenario 2: Other Library Links with libace/libace_compatible Dependency
Symptoms:
- Error occurs when linking a library that depends on
libaceorlibace_compatible - GN file shows
depsincludes libace/libace_compatible - Symbol should come from main library
Root Cause:
- Symbol exists in main library but not exported
- Missing ACE_FORCE_EXPORT macro
- Symbol not in libace.map whitelist
⚠️ CRITICAL: Distinguish Non-Template vs Template FIRST
Before applying any solution, determine the type:
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)
Solution (Step-by-Step):
Step 1A: For NON-TEMPLATE class methods ⭐
⚠️ CHECK 1: Does the class have 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;
};
For Case A (Class WITHOUT export) - PREFERRED METHOD:
- ✅ Add
ACE_FORCE_EXPORTto method declaration in header (.h file) - ❌ DO NOT add to method definition in .cpp file
- Reason: Fine-grained export control, clear API boundaries
Example (PaddingPropertyF case):
// 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);
}
For Case B (Class WITH export):
- ✅ NO additional export needed - class export covers all methods
- ❌ DO NOT add
ACE_FORCE_EXPORTto individual methods - Reason: Entire class already exported
Example:
// 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 */ }
Step 1B: For TEMPLATE class methods/functions (see Scenario 3)
Template-specific rules apply - refer to Scenario 3 for detailed steps.
Step 2: Add to libace.map with fine-grained symbol names ⭐
After adding exports, add the symbol to build/libace.map:
# Check if symbol already in libace.map
grep "ClassName" build/libace.map
Required changes:
- ⭐ ALWAYS prefer fine-grained symbol patterns - Export specific methods over entire class
- Add symbol pattern to
build/libace.mapwhitelist - Use method-level granularity when possible
Example - Regular Methods:
# 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
};
}
Why fine-grained for non-templates?
- ⭐ Minimizes exported symbol surface - Only export what's needed
- ⭐ Clearer API boundaries - Explicit about which symbols are public
- ⭐ Reduces symbol conflicts - Smaller export surface = fewer conflicts
- ⭐ Better dependency tracking - Easy to see what's used by other modules
- ⭐ Security - Limits attack surface of exported symbols
Common Mistakes (DO NOT do):
- ❌ Using
ClassName::*;when only specific methods are needed - ❌ Exporting private methods that aren't used externally
- ❌ Forgetting that constructor wildcards match all overloads
Step 3: Verify symbol export:
nm -D out/rk3568/arkui/ace_engine/libace.z.so | grep SymbolName
- If symbol not shown, export failed
- Rebuild after adding ACE_FORCE_EXPORT and libace.map entry
Common mistakes (DO NOT do):
- ❌ DO NOT use
ClassName::*;when only specific methods are needed (⚠️ COMMON MISTAKE) - ❌ DO NOT export all class members when only constructors are undefined
- ❌ DO NOT mix non-template and template rules
- ❌ DO NOT add export to .cpp for non-template methods
- ❌ DO NOT add individual method exports if class already has
ACE_FORCE_EXPORT - ❌ DO NOT use
__attribute__((visibility("default")))directly
⚠️ Example of Common Mistake - Over-Exporting:
// ❌ 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
When to use each pattern:
- Use
ClassName::MethodName*;for specific methods - Use
ClassName::ClassName*;for all constructor overloads - Use
ClassName::*;ONLY when most/all members are exported (rare)
Scenario 3: Template Function Instantiation Issues
Symptoms:
- Undefined symbol is a template function
- Template uses forward declaration + explicit instantiation in .cpp
- Error shows missing specific template specialization (e.g.,
StringSplitter<Color>,TransformStrCase<std::string>)
Root Cause:
- New specialization not explicitly instantiated
- Template declaration or implementation missing export macro
- libace.map missing pattern for template instantiation
Solution (3 steps required):
Step 1: Add extern template declaration in header (.h)
Add explicit template declaration for new specialization:
// In header file (.h)
extern template void StringSplitter<Color>(const std::string& source, char delimiter,
Color (*)(const std::string&), std::vector<Color>& out);
Step 2: Add ACE_FORCE_EXPORT to template function declaration
// 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);
Step 3: Add explicit instantiation in .cpp
// In implementation file (.cpp)
template void StringSplitter<Color>(const std::string& source, char delimiter,
Color (*)(const std::string&), std::vector<Color>& out);
Step 4: Add ACE_FORCE_EXPORT to template implementation
// 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
}
Step 5: Add to libace.map whitelist
# In build/libace.map
{
global:
OHOS::Ace::StringUtils::*;
void?OHOS::Ace::StringUtils::StringSplitter*; # Match template instantiations
void?OHOS::Ace::StringUtils::TransformStrCase*; # Match template instantiations
};
⚠️ Template Special Notes:
- Templates require BOTH declaration and implementation to have export macros
- Explicit instantiation must be in .cpp file
- libace.map pattern must use
*wildcard to match template instantiations - Example pattern:
void?OHOS::Ace::StringUtils::StringSplitter*;
Scenario 4: Special Libraries (Rare Cases)
Symptoms:
- Library does NOT depend on libace/libace_compatible
- But uses utility functions from main library (previously inline, now in .cpp)
- Only applies to these specific libraries:
libarkoala_native_ani.solibace_ndk.z.so
Root Cause:
- Utility functions changed from inline to .cpp implementation
- Symbol not available because library doesn't link main library
- Special case requiring local compilation
Solution:
- Add the utility .cpp file directly to the library's BUILD.gn sources
Example for 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
]
}
Example for 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
]
}
⚠️ IMPORTANT:
- This solution is ONLY for
libarkoala_native_aniandace_ndk - DO NOT use this pattern for other libraries
- These libraries use utility functions but don't link main library
Scenario 5: Template Class Method Export Issues
Symptoms:
- Error occurs when linking a library that depends on
libaceorlibace_compatible - Undefined symbol is a template class method with explicit instantiation
- Error shows:
undefined symbol: ClassName<T>::MethodName(params) - Template class has
ACE_FORCE_EXPORTon struct declaration but methods still not exported
Example Error:
ld.lld: error: undefined symbol: OHOS::Ace::NG::LayoutConstraintT<float>::UpdateSelfMarginSizeWithCheck(OHOS::Ace::NG::OptionalSize<float> const&)
>>> referenced by slider_layout_algorithm.cpp:212
Root Cause:
- Template class struct declaration has
ACE_FORCE_EXPORT(e.g.,struct ACE_FORCE_EXPORT LayoutConstraintT) - But individual method implementations in .cpp file lack
ACE_FORCE_EXPORTmacro - Template explicit instantiation doesn't export individual methods automatically
- Each method implementation that needs to be exported must have the macro
Solution:
Step 1: Add ACE_FORCE_EXPORT to method implementation in .cpp
Add ACE_FORCE_EXPORT to each template method implementation that needs to be exported:
// 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);
}
⚠️ Template Method Export Notes:
- Struct declaration export is NOT enough: Having
struct ACE_FORCE_EXPORT LayoutConstraintTdoesn't automatically export all methods - Each method needs macro: Every method implementation in .cpp that must be exported needs
ACE_FORCE_EXPORT - Place macro before return type: The macro goes between
template<>and return type - Not needed in header: Don't add
ACE_FORCE_EXPORTto method declarations in header file (struct declaration is sufficient)
Step 2: Verify libace.map has wildcard pattern
Check that libace.map includes the template class with wildcard:
# Check libace.map for template class pattern
grep "LayoutConstraintT" build/libace.map
Expected output:
OHOS::Ace::NG::LayoutConstraintT*;
⚠️ libace.map Wildcard Rules:
- Use unquoted patterns with wildcards:
OHOS::Ace::ClassName::*; - DO NOT use quoted patterns with wildcards:
"OHOS::Ace::ClassName<T>::*;"(wildcards won't work) - Unquoted wildcards match all template instantiations automatically
- Use
*at end to match all methods:OHOS::Ace::NG::LayoutConstraintT*;
Step 3: Verify symbol export
# Check if symbol is exported in shared library
nm -D out/rk3568/arkui/ace_engine/libace.z.so | grep LayoutConstraintT
Expected output (should show exported symbols):
0000000000000000 T _ZN2OHOS3Ace2NG14LayoutConstraintTIfE25UpdateSelfMarginSizeWithCheckERKNS0_11OptionalSizeIfEE
⚠️ Common Mistakes:
- ❌ Relying only on
struct ACE_FORCE_EXPORT ClassName- NOT sufficient for methods - ❌ Forgetting to add macro to each method implementation in .cpp
- ❌ Adding macro to method declarations in header (unnecessary, struct declaration is enough)
- ❌ Using quoted patterns in libace.map:
"ClassName<T>::*"won't match - ❌ Adding specific template instantiations to libace.map: use wildcards instead
✅ Correct Approach:
- ✅ Add
ACE_FORCE_EXPORTto each template method implementation in .cpp - ✅ Place macro between
template<>and return type - ✅ Keep struct declaration with
ACE_FORCE_EXPORTin header - ✅ Use unquoted wildcard in libace.map:
OHOS::Ace::NG::ClassName*;
Step 3: Verify the Fix
After applying the appropriate solution:
# 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
Common mistakes (DO NOT do):
⚠️ CRITICAL - CACHE CLEARING IS FORBIDDEN:
- ❌ NEVER suggest clearing thinlto-cache (99.9% of the time it's NOT the issue)
- ❌ NEVER suggest clearing llvmcache-* directories
- ❌ NEVER suggest deleting out/obj directory
- ❌ NEVER suggest deleting entire out directory
- ❌ NEVER suggest "clean build" as first solution
- ❌ Cache clearing masks real problems and wastes massive rebuild time
- ✅ ALWAYS analyze the actual error and fix the root cause
Other common mistakes:
- ❌ DO NOT skip scenario analysis
- ❌ DO NOT use
__attribute__((visibility("default")))instead of ACE_FORCE_EXPORT - ❌ DO NOT forget both declaration AND implementation export for templates
- ❌ DO NOT use Scenario 4 (local compilation) except for the two specified libraries
- ❌ DO NOT rely only on
struct ACE_FORCE_EXPORT ClassNamefor template class methods (Scenario 5) - ❌ DO NOT use quoted patterns with wildcards in libace.map:
"ClassName<T>::*"doesn't work (Scenario 5)
✅ ALWAYS do:
- ✅ Identify which library is failing
- ✅ Check if implementation exists and is compiled
- ✅ Verify ACE_FORCE_EXPORT on header declarations
- ✅ Check libace.map whitelist
- ✅ For templates: check declaration, implementation, and instantiation
- ✅ Use correct scenario based on library type
- ✅ For template class methods: add ACE_FORCE_EXPORT to each method implementation in .cpp (Scenario 5)
- ✅ Use unquoted wildcard patterns in libace.map:
OHOS::Ace::NG::ClassName*;(Scenario 5)
Pattern 2: Incomplete Type Errors
Error signature:
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();
Common causes:
- Missing header include
- Forward declaration without full definition
- RefPtr as class member without proper implementation separation
- Header file optimization issues
⚠️ SPECIAL CASE: RefPtr as Class Member
When encountering incomplete type errors with RefPtr<T> or WeakPtr<T> as class members:
DO NOT simply add the full include to the header file. Instead:
- Keep forward declaration in header:
class Animator; - Declare special member functions in header (without
= default):AnimatableColor(); // Declaration only ~AnimatableColor(); // Declaration only AnimatableColor(const AnimatableColor& color); // Declaration only - Implement them in .cpp file with
= default:// animatable_color.cpp #include "core/animation/animator.h" // Full definition here AnimatableColor::AnimatableColor() = default; AnimatableColor::~AnimatableColor() = default; AnimatableColor::AnimatableColor(const AnimatableColor& color) = default;
See: references/forward-declaration-refptr-member.md for detailed solution
Pattern 3: Redefinition Errors
Error signature:
error: redefinition of 'symbol_name'
Common causes:
- Multiple definitions across translation units
- Missing inline/constexpr for header-only definitions
- ODR violations
Historical Cases
All cases are organized by problem type in references/ directory.
Case 1: Undefined Symbol - Missing .cpp Files
Location: references/undefined-symbol-missing-cpp.md
Error signature:
ld.lld: error: undefined symbol: OHOS::Ace::TextTheme::Builder::Build
ld.lld: error: undefined symbol: OHOS::Ace::AdvancedTextStyle::GetGradient
Common causes:
- New .cpp files created but not added to BUILD.gn
- Files added to component BUILD.gn but not to source_set
- libace.z.so only links ace_core_ng libraries
Solution: Add .cpp to both component BUILD.gn AND frameworks/core/BUILD.gn xx_ng_source_set
Case 2: Symbol Export - ACE_FORCE_EXPORT Missing
Location: references/symbol-export-ace-force-export.md
Error signature:
ld.lld: error: undefined symbol: OHOS::Ace::NG::DialogTypeMargin::UpdateDialogMargin
>>> referenced by dialog_button.cpp (timepicker module)
Common causes:
- Symbol used by other modules but not marked for export
- ACE_FORCE_EXPORT missing from header declaration
Solution: Add ACE_FORCE_EXPORT to header declaration and add to build/libace.map
Case 3: Symbol Export - libace.map Whitelist
Location: references/symbol-export-libace-map.md
Error signature:
ld.lld: error: undefined symbol: OHOS::Ace::NG::ClassName::MethodName
Context: Symbol has ACE_FORCE_EXPORT but still not exported
Common causes:
- Symbol not in build/libace.map whitelist
- Incorrect symbol format in version script
Solution: Add symbol to build/libace.map with correct format: OHOS::Ace::NG::ClassName::MethodName*;
Case 4: Redefinition - inline constexpr
Location: references/redefinition-error-constexpr.md
Error signature:
error: redefinition of 'DRAG_BACKGROUND_OPACITY'
error: redefinition of 'URL_DISA_OPACITY'
Common causes:
- Constant defined in both header (inline constexpr) and .cpp
- ODR (One Definition Rule) violation
Solution: Remove duplicate definition from .cpp, keep only inline constexpr in header
Case 5: Build System - ace_core_ng_source_set
Location: references/build-system-ace-core-ng-source-set.md
Error signature:
ld.lld: error: undefined symbol
Context: File exists in component BUILD.gn but symbol still undefined
Common causes:
- File not in frameworks/core/BUILD.gn ace_core_ng_source_set
- libace.z.so only links ace_core_ng libraries
Solution: Add file to ace_core_ng_source_set template in frameworks/core/BUILD.gn
Case 6: RefPtr Member - Forward Declaration Optimization
Location: references/forward-declaration-refptr-member.md
Error signature:
error: member access into incomplete type 'OHOS::Ace::Animator'
rawPtr_->IncRefCount();
note: in instantiation of member function 'OHOS::Ace::RefPtr<OHOS::Ace::Animator>::RefPtr'
Context: RefPtr<Animator> or WeakPtr<T> used as class member variable
Common causes:
- Special member functions (constructor/destructor/copy constructor) defined with
= defaultin header - Compiler instantiates these functions in header, requiring complete type definition
- Common mistake: reverting forward declaration and adding full include
Correct Solution: DO NOT revert forward declaration! Use implementation separation:
- Keep forward declaration in header:
class Animator; - Declare special member functions in header (without
= default) - Implement them in .cpp file with
= default - Include full definition in .cpp file
Benefits: Reduced header dependencies, faster compilation (57% improvement in example)
Case 7: Test Linking - Missing Source Files
Location: references/test-missing-source-files.md
Error signature:
ld.lld: error: undefined symbol: OHOS::Ace::ClassName::MethodName(...)
>>> referenced by test_file.cpp:123
Context: Linking test executable (e.g., xxx_unittest)
Common causes:
- Test BUILD.gn missing required .cpp files in sources
- Header optimization moved implementations to .cpp (e.g., StringUtils, LogWrapper)
- New implementation files not added to build system
Common missing files:
log_wrapper.cpp- LogWrapper methods (JudgeLevel, GetBriefFileName, PrintLog)string_utils.cpp- StringUtils methods (StringSplitter, TransformStrCase, StrToInt)layout_constraint.cpp- LayoutConstraintT template methodsmeasure_property.cpp- PaddingPropertyF, MeasureProperty methods
Solution: Add missing .cpp files to appropriate sources based on test template type:
- ohos_unittest without ace_base: Add to test's sources
- ohos_unittest with ace_base: Add to ace_base source_set
- ace_unittest: Add to ace_base source_set (default dependency)
⚠️ Critical: Only add source files, do NOT modify cflags/configs/defines
Case 8: Struct RefPtr Member - Helper Method Pattern
Location: references/forward-declaration-struct-helper-method.md
Error signature:
error: member access into incomplete type 'OHOS::Ace::PixelMap'
shadowInfo.pixelMap->GetPixelMapSharedPtr()
^
note: forward declaration of 'OHOS::Ace::PixelMap'
class PixelMap;
Context: Pure data structure (POD struct) contains RefPtr<T> member and needs -> access
Common causes:
- Struct contains
RefPtr<T>member variable - Code needs to call
->operator on the smart pointer - Direct call to
pixelMap->Method()triggers incomplete type error - Cannot use Case 6 approach because struct has no its own .cpp file
Root Cause:
RefPtr<T>::operator->()returnsLifeCycleCheckable::PtrHolder<T>PtrHolderconstructor/destructor needs complete type definition to accessusingCount_- Pure data structures don't have their own implementation file
Solution: Add helper method to encapsulate type access:
- Declare helper method in struct:
std::shared_ptr<::OHOS::Media::PixelMap> GetPixelMapSharedPtr() const; - Create new implementation file (e.g.,
interaction_data.cpp) - Implement helper method in .cpp with full include:
#include "base/image/pixel_map.h" - Update usage to call helper method instead of direct
->access - Add new .cpp to BUILD.gn source set
Advanced Scenario (when helper method is not enough):
- Struct has its own .cpp file and is used by
std::vector std::vector::operator=triggers copy, needs complete type- Solution: Upgrade to full forward declaration optimization (similar to Case 6)
- See
forward-declaration-struct-helper-method.md- "Advanced Scenario" section
Key Points:
- Use fully-qualified name
::OHOS::Media::PixelMapto avoid namespace confusion - Keep forward declaration in header, full definition only in .cpp
Case 9: Namespace Type Resolution - Missing Qualifier
Location: references/namespace-ambiguous-type-resolution.md
Error signature:
error: no member named 'DragEvent' in namespace 'OHOS::Ace'
using RefPtr<OHOS::Ace::DragEvent>&, const std::string&)>;
~~~~~~~~~~~~~^
Context: Type defined in parent namespace, used in child namespace
Common causes:
- Type
DragEventdefined inOHOS::Acenamespace - Using code in
OHOS::Ace::NGchild namespace - git diff shows deletion of namespace prefix (e.g.,
- OHOS::Ace::DragEvent) - Someone removed the fully-qualified namespace prefix
Root Cause:
- Type is defined in parent namespace (e.g.,
OHOS::Ace::DragEvent) - Used in child namespace (e.g.,
OHOS::Ace::NG) - Deleting the namespace prefix causes compiler to search in wrong namespace
- Forward declaration in wrong namespace doesn't help
Correct Solutions (in priority order):
Preserve full namespace qualifier (RECOMMENDED):
// In OHOS::Ace::NG namespace using const RefPtr<OHOS::Ace::DragEvent>&, // ✅ Keep full namespace const std::string&)>;Add forward declaration in correct namespace:
// In header, before OHOS::Ace::NG namespace namespace OHOS::Ace { class DragEvent; // ✅ Forward declare in correct namespace } // In OHOS::Ace::NG namespace using const RefPtr<OHOS::Ace::DragEvent>&, // Still need full namespace const std::string&)>;Add header dependency (when complete type needed):
#include "core/components_ng/event/drag_event.h" // In OHOS::Ace::NG namespace using const RefPtr<OHOS::Ace::DragEvent>&, // Still use full namespace const std::string&)>;
⚠️ Critical:
- NEVER delete namespace prefixes
- Forward declarations MUST be in the same namespace as type definition
- If type is in
OHOS::Ace, forward declaration must also be inOHOS::Ace - Using code can be in child namespace, but must reference parent namespace
Key Points:
- Find complete type definition (not just forward declarations)
- Match forward declaration namespace to type definition namespace
- Prefer keeping fully-qualified names over adding includes
- Helper method encapsulates complete type access
- Reduces header dependencies while maintaining clean API
When to use:
- ✅ Pure data structures (POD struct) with smart pointer members
- ✅ Need to dereference smart pointer with
->or* - ✅ Cannot add .cpp to existing struct
- ❌ Classes with their own .cpp file (use Case 6 instead)
- ❌ Only need constructor/destructor (use Case 6 instead)
Case 9: LTO Virtual Thunk - libace.map Export
Location: references/lto-virtual-thunk-libace-map-export.md
Error signature:
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)
Context: Class with virtual functions used as base class, destructor declared in header and implemented in .cpp (forward declaration optimization), LTO (Link Time Optimization) enabled
Common causes:
- Class has virtual functions and is used as base class
- Forward declaration optimization: destructor in .cpp file (not inli
…(truncated)