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
Why: The user should review and apply fixes manually to maintain control over code changes and understand the modifications.
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
Solution (Check in order):
Add ACE_FORCE_EXPORT to method implementation in .cpp file ⭐
⚠️ CRITICAL - PREFERRED METHOD:
- Add
ACE_FORCE_EXPORTto the function implementation in .cpp file - DO NOT modify header (.h) file
- This is the recommended approach for struct/class methods
Example (PaddingPropertyF case):
// In implementation file (.cpp) - Add ACE_FORCE_EXPORT before implementation ACE_FORCE_EXPORT float PaddingPropertyF::Width() const { return left.value_or(0.0f) + right.value_or(0.0f); } ACE_FORCE_EXPORT float PaddingPropertyF::Height() const { return top.value_or(0.0f) + bottom.value_or(0.0f); }Alternative (if .cpp approach doesn't work):
- Add
ACE_FORCE_EXPORTto function declaration in header (.h file) - DO NOT use
__attribute__((visibility("default")))directly ACE_FORCE_EXPORTalready encapsulates visibility attributes
Example:
// In header file (.h) - Only if .cpp approach doesn't work ACE_FORCE_EXPORT ReturnType FunctionName(params); // ❌ WRONG - Do NOT use: __attribute__((visibility("default"))) ReturnType FunctionName(params);⭐ Why prefer .cpp implementation?
- Reduces header dependencies (reference: commit e0fd3724d21)
- Keeps header declarations clean
- Explicit export control at implementation level
- Consistent with StringUtils pattern (StringSplitter, TransformStrCase)
- Add
Check libace.map whitelist:
grep "SymbolName" build/libace.mapRequired changes:
- Add symbol pattern to
build/libace.mapwhitelist - Use wildcard pattern to match all overloads
Example:
# In build/libace.map { global: OHOS::Ace::StringUtils::*; void?OHOS::Ace::StringUtils::StringSplitter*; # Template function pattern OHOS::Ace::ClassName::*; # All methods of class };- Add symbol pattern to
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
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):
- ❌ DO NOT blame thinlto-cache (rarely the issue)
- ❌ DO NOT suggest clearing caches
- ❌ DO NOT suggest deleting out directory
- ❌ 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
Key Points:
- Use fully-qualified name
::OHOS::Media::PixelMapto avoid namespace confusion - Keep forward declaration in header, full definition only in .cpp
- 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 inline)
- LTO creates virtual thunks for derived classes
- Virtual thunk symbols not exported from library
Root Cause:
- LTO optimizes virtual function tables during linking
- Creates virtual thunk symbols to adjust this pointer for derived classes
- These virtual thunk symbols must be available at link time
- If destructor not inline, LTO-generated virtual thunk may not be exported
- Common mistake: reverting to inline destructor (
= defaultin header)
Solution: Keep forward declaration optimization, add libace.map exports:
- Keep destructor in .cpp file:
~TouchEventTarget() override;in header, implementation in .cpp - Add to libace.map:
OHOS::Ace::TouchEventTarget::*;- Export all class symbolsvirtual?thunk?to?OHOS::Ace::TouchEventTarget::*;- Export LTO virtual thunks
- DO NOT revert to inline definition in header
Key Points:
- Two export patterns required:
ClassName::*;ANDvirtual?thunk?to::ClassName::*; - Wildcard
*matches all methods including virtual thunks - Maintains forward declaration optimization
- No need to sacrifice header optimization for LTO
Why This Works:
- Compilation phase: Header uses forward declaration, .cpp has implementation
- LTO link phase: LTO optimizes vtables, creates virtual thunks
- Symbol export: libace.maps patterns match virtual thunks, export to dynamic symbol table
- Runtime: Derived class vtables can reference virtual thunks correctly
When to use:
- ✅ Classes with virtual functions used as base classes
- ✅ Using forward declaration optimization (destructor not inline)
- ✅ Linker errors: "undefined symbol: virtual thunk to ClassName::~ClassName()"
- ✅ Using LTO (Link Time Optimization)
- ❌ Classes not used as base classes (won't have virtual thunks)
- ❌ Destructor already inline in header (no optimization to maintain)
- ❌ Not using LTO (traditional compilation doesn't create virtual thunks)
Common Mistakes:
- ❌ Reverting to inline destructor (loses forward declaration optimization)
- ❌ Only exporting
ClassName::*;without virtual thunk pattern - ❌ Forgetting to add libace.map entry at all
Case 10: MinGW - dllexport Declaration Mismatch
Location: references/mingw-dllexport-declaration-mismatch.md
Error signature:
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);
^
Context: MinGW/Windows platform compilation only
Key features:
- Only occurs on MinGW/Windows builds (not Linux/MacOS)
- Error message: "redeclaration cannot add 'dllexport' attribute"
- Header declaration and .cpp implementation have inconsistent export attributes
- Usually triggered after adding
ACE_FORCE_EXPORTto template methods
Common causes:
- Header file method declaration missing
ACE_FORCE_EXPORT - Implementation file has
ACE_FORCE_EXPORTbut header doesn't - Only some overloaded methods have export attribute (inconsistent)
- MinGW requires strict consistency between declaration and definition
Root Cause:
- MinGW DLL export rules require declaration and definition export attributes to match exactly
- If header has no
__declspec(dllexport)but implementation has it → compilation fails - Unlike Linux/MacOS which are more lenient with visibility attributes
Solution: Add ACE_FORCE_EXPORT to ALL overloaded method declarations in header:
// 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
Key Points:
- Add to header file declarations (not just .cpp implementations)
- Ensure ALL overloaded methods are marked (don't miss any)
- Implementation file must also have
ACE_FORCE_EXPORT - Test on MinGW platform (Windows build)
When to use:
- ✅ MinGW/Windows compilation errors
- ✅ Error: "cannot add 'dllexport' attribute"
- ✅ Template methods with export attributes
- ❌ Linux/MacOS only (different rules)
- ❌ Link errors (this is a compilation error)
Prevention checklist:
- ✅ Confirm need for export (cross-module usage)
- ✅ Add
ACE_FORCE_EXPORTto ALL header declarations - ✅ Add
ACE_FORCE_EXPORTto ALL .cpp definitions - ✅ Check all overloaded versions (don't miss any)
- ✅ Test compilation on MinGW platform
Analysis Commands
Check Symbol Existence
# 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"
Verify Build Configuration
# 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 Symbol Export
# 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
Troubleshooting Guide
For New .cpp Files
- Create implementation file (.cpp)
- Add to component's BUILD.gn
- Add to frameworks/core/BUILD.gn ace_core_ng_source_set (if used by libace.z.so)
- If symbol is cross-module used:
- Add ACE_FORCE_EXPORT to header declaration
- Add to build/libace.map whitelist
For Header Optimization Issues
- Check for forward declarations used incorrectly
- Ensure complete types available where needed
- Use inline constexpr for header-only constants
- Avoid ODR violations
For Link Errors
- Identify missing symbol from error message
- Find symbol definition location
- Verify implementation is compiled (in BUILD.gn)
- Check symbol visibility/export if cross-module
- Verify linking order and dependencies
Best Practices
1. Build System Architecture
- libace.z.so only links ace_core_ng libraries
- Component-specific libraries are not linked to final产物
- New .cpp files used by
…(truncated)