@@ -8,6 +8,169 @@ plugins {
88 id ' de.undercouch.download' version ' 4.1.1'
99}
1010
11+ // Helper function to check if objcopy is available
12+ def checkObjcopyAvailable () {
13+ try {
14+ def process = [' objcopy' , ' --version' ]. execute()
15+ process. waitFor()
16+ return process. exitValue() == 0
17+ } catch (Exception e) {
18+ return false
19+ }
20+ }
21+
22+ // Helper function to check if dsymutil is available (for macOS)
23+ def checkDsymutilAvailable () {
24+ try {
25+ def process = [' dsymutil' , ' --version' ]. execute()
26+ process. waitFor()
27+ return process. exitValue() == 0
28+ } catch (Exception e) {
29+ return false
30+ }
31+ }
32+
33+ // Helper function to check if debug extraction should be skipped
34+ def shouldSkipDebugExtraction () {
35+ // Skip if explicitly disabled
36+ if (project. hasProperty(' skip-debug-extraction' )) {
37+ return true
38+ }
39+
40+ // Skip if required tools are not available
41+ if (os(). isLinux() && ! checkObjcopyAvailable()) {
42+ return true
43+ }
44+
45+ if (os(). isMacOsX() && ! checkDsymutilAvailable()) {
46+ return true
47+ }
48+
49+ return false
50+ }
51+
52+ // Helper function to get debug file path for a given config
53+ def getDebugFilePath (config ) {
54+ def extension = os(). isLinux() ? ' so' : ' dylib'
55+ return file(" $buildDir /lib/main/${ config.name} /${ osIdentifier()} /${ archIdentifier()} /debug/libjavaProfiler.${ extension} .debug" )
56+ }
57+
58+ // Helper function to get stripped file path for a given config
59+ def getStrippedFilePath (config ) {
60+ def extension = os(). isLinux() ? ' so' : ' dylib'
61+ return file(" $buildDir /lib/main/${ config.name} /${ osIdentifier()} /${ archIdentifier()} /stripped/libjavaProfiler.${ extension} " )
62+ }
63+
64+ // Helper function to create error message for missing tools
65+ def getMissingToolErrorMessage (toolName , installInstructions ) {
66+ return """
67+ |${ toolName} is not available but is required for split debug information.
68+ |
69+ |To fix this issue:
70+ |${ installInstructions}
71+ |
72+ |If you want to build without split debug info, set -Pskip-debug-extraction=true
73+ """ . stripMargin()
74+ }
75+
76+ // Helper function to create debug extraction task
77+ def createDebugExtractionTask (config , linkTask ) {
78+ return tasks. register(' extractDebugLibRelease' , Exec ) {
79+ onlyIf {
80+ ! shouldSkipDebugExtraction()
81+ }
82+ dependsOn linkTask
83+ description = ' Extract debug symbols from release library'
84+ workingDir project. buildDir
85+
86+ // Declare outputs so Gradle knows what files this task creates
87+ outputs. file getDebugFilePath(config)
88+
89+ doFirst {
90+ def sourceFile = linkTask. get(). linkedFile. get(). asFile
91+ def debugFile = getDebugFilePath(config)
92+
93+ // Ensure debug directory exists
94+ debugFile. parentFile. mkdirs()
95+
96+ // Set the command line based on platform
97+ if (os(). isLinux()) {
98+ commandLine = [' objcopy' , ' --only-keep-debug' , sourceFile. absolutePath, debugFile. absolutePath]
99+ } else {
100+ // For macOS, we'll use dsymutil instead
101+ commandLine = [' dsymutil' , sourceFile. absolutePath, ' -o' , debugFile. absolutePath. replace(' .debug' , ' .dSYM' )]
102+ }
103+ }
104+ }
105+ }
106+
107+ // Helper function to create debug link task (Linux only)
108+ def createDebugLinkTask (config , linkTask , extractDebugTask ) {
109+ return tasks. register(' addDebugLinkLibRelease' , Exec ) {
110+ onlyIf {
111+ os(). isLinux() && ! shouldSkipDebugExtraction()
112+ }
113+ dependsOn extractDebugTask
114+ description = ' Add debug link to the original library'
115+
116+ doFirst {
117+ def sourceFile = linkTask. get(). linkedFile. get(). asFile
118+ def debugFile = getDebugFilePath(config)
119+
120+ commandLine = [' objcopy' , ' --add-gnu-debuglink=' + debugFile. absolutePath, sourceFile. absolutePath]
121+ }
122+ }
123+ }
124+
125+ // Helper function to create debug file copy task
126+ def createDebugCopyTask (config , extractDebugTask ) {
127+ return tasks. register(' copyReleaseDebugFiles' , Copy ) {
128+ onlyIf {
129+ ! shouldSkipDebugExtraction()
130+ }
131+ dependsOn extractDebugTask
132+ from file(" $buildDir /lib/main/${ config.name} /${ osIdentifier()} /${ archIdentifier()} /debug" )
133+ into file(libraryTargetPath(config. name))
134+ include ' **/*.debug'
135+ include ' **/*.dSYM/**'
136+ }
137+ }
138+
139+ // Main function to setup debug extraction for release builds
140+ def setupDebugExtraction (config , linkTask ) {
141+ if (config. name == ' release' && config. active && ! project. hasProperty(' skip-native' )) {
142+ // Create all debug-related tasks
143+ def extractDebugTask = createDebugExtractionTask(config, linkTask)
144+ def addDebugLinkTask = createDebugLinkTask(config, linkTask, extractDebugTask)
145+
146+ // Create the strip task and configure it properly
147+ def stripTask = tasks. register(' stripLibRelease' , StripSymbols ) {
148+ // No onlyIf needed here - setupDebugExtraction already handles the main conditions
149+ dependsOn addDebugLinkTask
150+ }
151+
152+ // Configure the strip task after registration
153+ stripTask. configure {
154+ targetPlatform = linkTask. get(). targetPlatform
155+ toolChain = linkTask. get(). toolChain
156+ binaryFile = linkTask. get(). linkedFile. get(). asFile
157+ outputFile = getStrippedFilePath(config)
158+ }
159+
160+ def copyDebugTask = createDebugCopyTask(config, extractDebugTask)
161+
162+ // Wire up the copy task to use stripped binaries
163+ def copyTask = tasks. findByName(" copyReleaseLibs" )
164+ if (copyTask != null ) {
165+ copyTask. dependsOn stripTask
166+ copyTask. inputs. files stripTask. get(). outputs. files
167+
168+ // Create an extra folder for the debug symbols
169+ copyTask. dependsOn copyDebugTask
170+ }
171+ }
172+ }
173+
11174def libraryName = " ddprof"
12175
13176description = " Datadog Java Profiler Library"
@@ -92,6 +255,8 @@ tasks.register('copyExternalLibs', Copy) {
92255 from(project. getProperty(" with-libs" )) {
93256 include " **/*.so"
94257 include " **/*.dylib"
258+ include " **/*.debug"
259+ include " **/*.dSYM/**"
95260 }
96261 into " ${ projectDir} /build/classes/java/main/META-INF/native-libs"
97262 }
@@ -105,7 +270,7 @@ def cloneAPTask = tasks.register('cloneAsyncProfiler') {
105270 if (! targetDir. exists()) {
106271 println " Cloning missing async-profiler git subdirectory..."
107272 exec {
108- commandLine ' git' , ' clone' , ' --branch' , branch_lock, ' --depth ' , ' 1 ' , ' https://github.com/datadog/async-profiler.git' , targetDir. absolutePath
273+ commandLine ' git' , ' clone' , ' --branch' , branch_lock, ' https://github.com/datadog/async-profiler.git' , targetDir. absolutePath
109274 }
110275 exec {
111276 workingDir targetDir. absolutePath
@@ -126,7 +291,10 @@ def cloneAPTask = tasks.register('cloneAsyncProfiler') {
126291 println " async-profiler commit hash differs (current: ${ currentCommit} , expected: ${ commit_lock} ), updating..."
127292 exec {
128293 workingDir targetDir. absolutePath
129- commandLine ' git' , ' fetch' , ' origin' , branch_lock, ' --depth' , ' 1'
294+ commandLine ' rm' , ' -rf' , targetDir. absolutePath
295+ }
296+ exec {
297+ commandLine ' git' , ' clone' , ' --branch' , branch_lock, ' https://github.com/datadog/async-profiler.git' , targetDir. absolutePath
130298 }
131299 exec {
132300 workingDir targetDir. absolutePath
@@ -273,6 +441,7 @@ buildConfigNames().each { name ->
273441 if (! project. hasProperty(' skip-native' )) {
274442 dependsOn copyTask
275443 }
444+
276445 from sourceSets. main. output. classesDirs
277446 from files(libraryTargetBase(name)) {
278447 include " **/*"
@@ -366,23 +535,7 @@ tasks.whenTaskAdded { task ->
366535 outputs. file linkedFile
367536 }
368537 if (config. name == ' release' ) {
369- def stripTask = tasks. register(' stripLibRelease' , StripSymbols ) {
370- onlyIf {
371- config. active
372- }
373- dependsOn linkTask
374- targetPlatform = tasks. linkLibRelease. targetPlatform
375- toolChain = tasks. linkLibRelease. toolChain
376- binaryFile = tasks. linkLibRelease. linkedFile. get()
377- outputFile = file(" $buildDir /lib/main/${ config.name} /${ osIdentifier()} /${ archIdentifier()} /stripped/libjavaProfiler.${ os().isLinux() ? 'so' : 'dylib'} " )
378- inputs. file binaryFile
379- outputs. file outputFile
380- }
381- def copyTask = tasks. findByName(" copyReleaseLibs" )
382- if (copyTask != null ) {
383- copyTask. dependsOn stripTask
384- copyTask. inputs. files stripTask. get(). outputs. files
385- }
538+ setupDebugExtraction(config, linkTask)
386539 }
387540 }
388541 }
@@ -454,6 +607,8 @@ tasks.register('javadocJar', Jar) {
454607 from javadoc. destinationDir
455608}
456609
610+
611+
457612tasks. register(' scanBuild' , Exec ) {
458613 workingDir " ${ projectDir} /src/test/make"
459614 commandLine ' scan-build'
0 commit comments