1 package org.apache.maven.plugin.cxx;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 import java.io.File;
22 import java.io.FileInputStream;
23 import java.io.FileOutputStream;
24 import java.io.InputStream;
25 import java.io.IOException;
26 import java.io.OutputStream;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.Map;
32 import java.util.Iterator;
33 import java.util.regex.Matcher;
34 import java.util.regex.Pattern;
35 import java.util.Set;
36 import java.util.Properties;
37
38 import org.codehaus.plexus.util.StringUtils;
39 import org.apache.commons.io.FilenameUtils;
40 import org.apache.commons.io.IOUtils;
41 import org.apache.commons.exec.CommandLine;
42 import org.apache.commons.exec.Executor;
43
44 import org.apache.maven.plugin.MojoExecutionException;
45
46
47 import org.apache.maven.shared.model.fileset.FileSet;
48 import org.apache.maven.shared.model.fileset.util.FileSetManager;
49 import org.apache.maven.artifact.Artifact;
50
51 import org.apache.maven.plugins.annotations.LifecyclePhase;
52 import org.apache.maven.plugins.annotations.Mojo;
53 import org.apache.maven.plugins.annotations.Parameter;
54
55
56
57
58
59
60
61 @Mojo( name = "cmake", defaultPhase = LifecyclePhase.GENERATE_SOURCES )
62 public class CMakeMojo extends AbstractLaunchMojo
63 {
64 @Override
65 protected List<String> getArgsList()
66 {
67 return null;
68 }
69
70
71
72
73
74
75
76 @Parameter( property = "cmake.projectdir" )
77 private String projectDir;
78
79 protected String getProjectDir()
80 {
81 if ( null == projectDir )
82 {
83 projectDir = new String( basedir.getAbsolutePath() );
84 }
85 return projectDir;
86 }
87
88
89
90
91
92
93 @Parameter( property = "cmake.generator", required = true )
94 private String generator;
95
96
97
98
99
100
101 @Parameter( property = "cmake.args" )
102 private String commandArgs;
103
104 protected String addCMakeDefinition( String sCMakeName, String sMavenValue )
105 {
106 String sResult = null;
107 if ( !StringUtils.isEmpty( sCMakeName ) && ! StringUtils.isEmpty( sMavenValue ) )
108 {
109 sResult = " -D" + sCMakeName + "=\"" + sMavenValue + "\"";
110 }
111 else
112 {
113 sResult = new String();
114 }
115 return sResult;
116 }
117
118 @Override
119 protected String getCommandArgs()
120 {
121 String result = new String();
122 if ( !StringUtils.isEmpty( commandArgs ) )
123 {
124 result = commandArgs + " ";
125 }
126 result += "\"" + getProjectDir() + "\" -G " + generator;
127 result += addCMakeDefinition( "CMAKE_BUILD_TYPE", buildConfig );
128 result += addCMakeDefinition( "TARGET_ID", project.getArtifactId() );
129 result += addCMakeDefinition( "TARGET_NAME", project.getName() );
130 result += addCMakeDefinition( "DEPENDENCY_DIR", getProjectDependenciesDirectory() );
131 result += addCMakeDefinition( "TARGET_VERSION", project.getVersion() );
132 result += addCMakeDefinition( "TARGET_CLASSIFIER", targetClassifier );
133 result += addCMakeDefinition( "TARGET_PLATFORM", targetPlatform );
134 result += addCMakeDefinition( "TARGET_ARCHITECTURE", targetArchitecture );
135 result += addCMakeDefinition( "EXECUTABLE_SUFFIX", executableSuffix );
136 result += addCMakeDefinition( "SHARED_LIBRARY_PREFIX", sharedLibraryPrefix );
137 result += addCMakeDefinition( "SHARED_LIBRARY_SUFFIX", sharedLibrarySuffix );
138 result += addCMakeDefinition( "SHARED_MODULE_PREFIX", sharedModulePrefix );
139 result += addCMakeDefinition( "SHARED_MODULE_SUFFIX", sharedModuleSuffix );
140 result += addCMakeDefinition( "STATIC_LIBRARY_PREFIX", staticLibraryPrefix );
141 result += addCMakeDefinition( "STATIC_LIBRARY_SUFFIX", staticLibrarySuffix );
142 result += addCMakeDefinition( "INJECT_MAVEN_DEPENDENCIES", injectMavenDependencies ? "true" : "false" );
143 return result;
144 }
145
146 @Override
147 protected String getExecutable()
148 {
149 return "cmake";
150 }
151
152
153
154
155
156
157
158 @Parameter()
159 private Map environmentVariables = new HashMap();
160
161 @Override
162 protected Map getMoreEnvironmentVariables()
163 {
164 return environmentVariables;
165 }
166
167 @Override
168 protected List<String> getSuccesCode()
169 {
170 return null;
171 }
172
173
174
175
176
177
178
179 @Parameter( property = "cmake.outsourcedir" )
180 private File outsourceDir;
181
182 @Override
183 protected File getWorkingDir()
184 {
185 if ( null == outsourceDir )
186 {
187 outsourceDir = new File( basedir.getPath() );
188 }
189 return outsourceDir;
190 }
191
192 @Override
193 public boolean isSkip()
194 {
195 return false;
196 }
197
198 protected String getProjectDependenciesDirectory()
199 {
200
201
202
203
204 String projectBuildDirectory = project.getBuild().getDirectory();
205 if ( StringUtils.isEmpty( projectBuildDirectory ) )
206 {
207 projectBuildDirectory = basedir.getAbsolutePath() + "/target";
208 }
209 return projectBuildDirectory + "/dependency";
210 }
211
212
213
214
215
216
217
218 @Parameter( property = "cmake.injectMavenDependencies", defaultValue = "true" )
219 private boolean injectMavenDependencies;
220
221
222
223
224
225
226 @Parameter( property = "cmake.mavenDependenciesFile", defaultValue = "CMakeMavenDependencies.txt" )
227 private String cmakeMavenDependenciesFile;
228
229
230
231
232
233
234 @Parameter( property = "cmake.dependenciesFile", defaultValue = "CMakeDependencies.txt" )
235 private String cmakeDependenciesFile;
236
237
238
239
240
241
242 @Parameter( property = "buildConfig", defaultValue = "" )
243 private String buildConfig;
244
245
246
247
248
249
250 @Parameter( property = "targetClassifier", defaultValue = "" )
251 private String targetClassifier;
252
253
254
255
256
257
258 @Parameter( property = "targetPlatform", defaultValue = "" )
259 private String targetPlatform;
260
261
262
263
264
265
266 @Parameter( property = "targetArchitecture", defaultValue = "" )
267 private String targetArchitecture;
268
269
270
271
272
273
274 @Parameter( property = "executableSuffix", defaultValue = "" )
275 private String executableSuffix;
276
277
278
279
280
281
282 @Parameter( property = "sharedLibraryPrefix", defaultValue = "" )
283 private String sharedLibraryPrefix;
284
285
286
287
288
289
290 @Parameter( property = "sharedLibrarySuffix", defaultValue = "" )
291 private String sharedLibrarySuffix;
292
293
294
295
296
297
298 @Parameter( property = "sharedModulePrefix", defaultValue = "" )
299 private String sharedModulePrefix;
300
301
302
303
304
305
306 @Parameter( property = "sharedModuleSuffix", defaultValue = "" )
307 private String sharedModuleSuffix;
308
309
310
311
312
313
314 @Parameter( property = "staticLibraryPrefix", defaultValue = "" )
315 private String staticLibraryPrefix;
316
317
318
319
320
321
322 @Parameter( property = "staticLibrarySuffix", defaultValue = "" )
323 private String staticLibrarySuffix;
324
325
326
327
328
329
330
331
332
333 @Parameter()
334 private String additionalDependenciesRoots[] = null;
335
336
337
338
339
340
341 @Parameter( )
342 protected String additionalIncludeRoots[] = null;
343
344
345 protected void findDependencies( Map<String, String> aiDependenciesRoots, List<String> aoDependenciesLib )
346 {
347 for ( Map.Entry<String, String> entry : aiDependenciesRoots.entrySet() )
348 {
349 String dependencyRoot = new String( entry.getKey() );
350
351 FileSet afileSet = new FileSet();
352 afileSet.setDirectory( dependencyRoot );
353
354 getLog().info( "Search for **/*.[so|dylib|dll|lib|a|] from " + afileSet.getDirectory() );
355 afileSet.setIncludes( Arrays.asList(
356 new String[]{"**/*.so", "**/*.dll", "**/*.dylib", "**/*.lib", "**/*.a"} ) );
357
358 FileSetManager aFileSetManager = new FileSetManager();
359 String[] found = aFileSetManager.getIncludedFiles( afileSet );
360
361 for ( int j = 0; null != found && j < found.length; j++ )
362 {
363
364 getLog().info( "Found dependencies Lib : " + found[j] );
365 getLog().info( "Found dependencies Lib full path : " + dependencyRoot + "/" + found[j] );
366 getLog().info( "Found dependencies Lib generalized path : " + entry.getValue()
367 + "/" + found[j] );
368 aoDependenciesLib.add( entry.getValue() + "/" + found[j] );
369 }
370 }
371 }
372
373 protected String baseNameAsStaticLibrary( String sName, boolean bMavenDependency )
374 {
375 if ( FilenameUtils.isExtension( sName, FilenameUtils.getExtension( staticLibrarySuffix ) ) )
376 {
377 sName = FilenameUtils.removeExtension( sName ) + ( bMavenDependency ? "${STATIC_LIBRARY_SUFFIX}" : "" );
378 if ( !StringUtils.isEmpty( staticLibraryPrefix ) )
379 {
380 if ( 0 == sName.indexOf( staticLibraryPrefix ) )
381 {
382 sName = sName.replaceFirst( Pattern.quote( staticLibraryPrefix ), "" );
383 sName = ( bMavenDependency ? "${STATIC_LIBRARY_PREFIX}" : "" ) + sName;
384 }
385 else
386 {
387 sName = "";
388 }
389 }
390 else
391 {
392 sName = ( bMavenDependency ? "${STATIC_LIBRARY_PREFIX}" : "" ) + sName;
393 }
394 }
395 return sName;
396 }
397
398 protected String baseNameAsSharedModule( String sName, boolean bMavenDependency )
399 {
400 if ( FilenameUtils.isExtension( sName, FilenameUtils.getExtension( sharedModuleSuffix ) ) )
401 {
402 sName = FilenameUtils.removeExtension( sName ) + ( bMavenDependency ? "${SHARED_MODULE_SUFFIX}" : "" );
403 if ( !StringUtils.isEmpty( sharedModulePrefix ) )
404 {
405 if ( 0 == sName.indexOf( sharedModulePrefix ) )
406 {
407 sName = sName.replaceFirst( Pattern.quote( sharedModulePrefix ), "" );
408 sName = ( bMavenDependency ? "${SHARED_MODULE_PREFIX}" : "" ) + sName;
409 }
410 else
411 {
412 sName = "";
413 }
414 }
415 else
416 {
417 sName = ( bMavenDependency ? "${SHARED_MODULE_PREFIX}" : "" ) + sName;
418 }
419 }
420 return sName;
421 }
422
423 protected String baseNameAsSharedLibrary( String sName, boolean bMavenDependency )
424 {
425 if ( FilenameUtils.isExtension( sName, FilenameUtils.getExtension( sharedLibrarySuffix ) ) )
426 {
427 sName = FilenameUtils.removeExtension( sName ) + ( bMavenDependency ? "${SHARED_LIBRARY_SUFFIX}" : "" );
428 if ( !StringUtils.isEmpty( sharedLibraryPrefix ) )
429 {
430 if ( 0 == sName.indexOf( sharedLibraryPrefix ) )
431 {
432 sName = sName.replaceFirst( Pattern.quote( sharedLibraryPrefix ), "" );
433 sName = ( bMavenDependency ? "${SHARED_LIBRARY_PREFIX}" : "" ) + sName;
434 }
435 else
436 {
437 sName = "";
438 }
439 }
440 else
441 {
442 sName = ( bMavenDependency ? "${SHARED_LIBRARY_PREFIX}" : "" ) + sName;
443 }
444 }
445 return sName;
446 }
447
448 protected String generalizeDependencyFileName( String dependency, boolean bMavenDependency )
449 {
450 String sName = FilenameUtils.getName( dependency );
451 String fullPath = FilenameUtils.getFullPathNoEndSeparator( dependency );
452
453 String sGeneralizedName = baseNameAsSharedLibrary( sName, bMavenDependency );
454 if ( StringUtils.isEmpty( sGeneralizedName ) )
455 {
456 sGeneralizedName = baseNameAsStaticLibrary( sName, bMavenDependency );
457 }
458 if ( StringUtils.isEmpty( sGeneralizedName ) )
459 {
460 sGeneralizedName = baseNameAsSharedModule( sName, bMavenDependency );
461 }
462
463 return ( bMavenDependency ? fullPath + "/" : "" )
464 + ( StringUtils.isEmpty( sGeneralizedName ) ? sName : sGeneralizedName );
465 }
466
467 protected boolean isDebugBuild()
468 {
469 return ( !StringUtils.isEmpty( buildConfig ) && 0 == buildConfig.indexOf( "deb" ) );
470 }
471
472 protected void updateOrCreateCMakeDependenciesFile( List aiDependenciesLib, boolean bMavenDependencies )
473 {
474 String dependencieFile = ( bMavenDependencies ? cmakeMavenDependenciesFile : cmakeDependenciesFile );
475 String fullDependenciesFile = dependencieFile;
476 File file = new File( dependencieFile );
477 if ( !file.isAbsolute() )
478 {
479
480 fullDependenciesFile = getProjectDir() + "/" + dependencieFile;
481 }
482 file = new File( fullDependenciesFile );
483
484 if ( !file.exists() )
485 {
486 try
487 {
488 file.createNewFile();
489 }
490 catch ( IOException e )
491 {
492 getLog().error( dependencieFile + " script can't be created at " + file.getAbsolutePath() );
493 return;
494 }
495 }
496
497
498 InputStream dependenciesStream = null;
499 String content = new String();
500 try
501 {
502 dependenciesStream = new FileInputStream( file );
503 content = IOUtils.toString( dependenciesStream, "UTF8" );
504 }
505 catch ( IOException e )
506 {
507
508 getLog().error( dependencieFile + " script can't be opened at " + file.getAbsolutePath() );
509 }
510 finally
511 {
512 getLog().debug( "close input stream at reading" );
513 IOUtils.closeQuietly( dependenciesStream );
514 }
515
516 String beginDepsPattern = ( bMavenDependencies
517 ? ( isDebugBuild() ? "# BEGIN MAVEN_DEBUG_DEPENDENCIES" : "# BEGIN MAVEN_OPTIMIZED_DEPENDENCIES" )
518 : "# BEGIN CMAKE_DEPENDENCIES" );
519 String endDepsPattern = ( bMavenDependencies
520 ? ( isDebugBuild() ? "# END MAVEN_DEBUG_DEPENDENCIES" : "# END MAVEN_OPTIMIZED_DEPENDENCIES" )
521 : "# END CMAKE_DEPENDENCIES" );
522
523 String beginIncPattern = "# BEGIN MAVEN_INCLUDE_ROOTS";
524 String endIncPattern = "# END MAVEN_INCLUDE_ROOTS";
525
526
527 if ( StringUtils.isEmpty( content ) || content.indexOf( beginDepsPattern ) == -1 )
528 {
529 getLog().info( file.getAbsolutePath() + " content full update" );
530 try
531 {
532 dependenciesStream = getClass().getResourceAsStream(
533 ( bMavenDependencies ? "/cmake-cpp-project/CMakeMavenDependencies.txt"
534 : "/cmake-cpp-project/CMakeDependencies.txt" ) );
535 content = IOUtils.toString( dependenciesStream, "UTF8" );
536 }
537 catch ( IOException e )
538 {
539 getLog().error( dependencieFile + " default content not found " );
540 }
541 finally
542 {
543 getLog().debug( "close input stream at full update" );
544 IOUtils.closeQuietly( dependenciesStream );
545 }
546 }
547
548
549 String simpleIndentation = "\n ";
550 String doubleIndentation = "\n ";
551 Iterator itDeps = aiDependenciesLib.iterator();
552 StringBuilder allDepsBuilder = new StringBuilder( ( bMavenDependencies
553 ? doubleIndentation : simpleIndentation ) );
554 while ( itDeps.hasNext() )
555 {
556 String dep = ( String ) itDeps.next();
557 if ( bMavenDependencies )
558 {
559 String externalDep = generalizeDependencyFileName( dep, true );
560 allDepsBuilder.append( "target_link_libraries(${target} "
561 + ( isDebugBuild() ? "debug " : "optimized " )
562 + externalDep + ")" + doubleIndentation );
563 }
564 else
565 {
566 String cmakeDep = generalizeDependencyFileName( dep, false );
567 allDepsBuilder.append(
568 "# If a \"" + cmakeDep + "\" target has been define, this means we are building "
569 + "an amalgamed cmake project" + simpleIndentation
570 + "# but maven dependencies can be used too" + simpleIndentation
571 + "if(TARGET " + cmakeDep + ")" + doubleIndentation
572 + "message(\"Adding direct " + cmakeDep + " cmake dependencies to target '${target}'\")"
573 + doubleIndentation
574 + "target_link_libraries(${target} " + cmakeDep + ")" + simpleIndentation
575 + "endif()" + simpleIndentation );
576 }
577 }
578
579
580 StringBuilder addIncsBuilder = new StringBuilder( doubleIndentation );
581 if ( bMavenDependencies && null != additionalIncludeRoots )
582 {
583 addIncsBuilder.append( "include_directories( " + doubleIndentation );
584 for ( String includeRoot : additionalIncludeRoots )
585 {
586 addIncsBuilder.append( "\"" + includeRoot + "\"" + doubleIndentation );
587 }
588 addIncsBuilder.append( ")" + doubleIndentation );
589 for ( String includeRoot : additionalIncludeRoots )
590 {
591 addIncsBuilder.append( "message(\"Adding '" + includeRoot + "' additional include root.\")"
592 + doubleIndentation );
593 }
594 }
595
596 getLog().debug( dependencieFile + " depfile was : " + content );
597 String allDeps = Matcher.quoteReplacement( allDepsBuilder.toString() );
598
599 getLog().debug( dependencieFile + " injected dependency will be : " + allDeps );
600
601 Pattern p1 = Pattern.compile( beginDepsPattern + ".*" + endDepsPattern, Pattern.DOTALL );
602 Matcher m1 = p1.matcher( content );
603 content = m1.replaceAll( beginDepsPattern + allDeps + endDepsPattern );
604
605 if ( bMavenDependencies && null != additionalIncludeRoots )
606 {
607 String addIncs = Matcher.quoteReplacement( addIncsBuilder.toString() );
608
609 getLog().debug( dependencieFile + " injected includes Roots will be : " + addIncs );
610 Pattern p2 = Pattern.compile( beginIncPattern + ".*" + endIncPattern, Pattern.DOTALL );
611 Matcher m2 = p2.matcher( content );
612 content = m2.replaceAll( beginIncPattern + addIncs + endIncPattern );
613 }
614
615 getLog().debug( dependencieFile + " depfile now is : " + content );
616 OutputStream outStream = null;
617 try
618 {
619 outStream = new FileOutputStream( file );
620 IOUtils.write( content, outStream, "UTF8" );
621 }
622 catch ( IOException e )
623 {
624 getLog().error( dependencieFile + " script can't be written at " + file.getAbsolutePath() + e.toString() );
625 }
626 finally
627 {
628 getLog().debug( "close output stream at update" );
629 IOUtils.closeQuietly( outStream );
630 }
631 }
632
633 protected String extractBuildConfig( String classifier )
634 {
635
636 String[] parts = classifier.split( "-" );
637 return ( parts.length >= 3 ) ? parts[parts.length - 1] : null;
638 }
639
640 protected String extractSubClassifier( String classifier )
641 {
642
643 String[] parts = classifier.split( "-" );
644 if ( parts.length >= 3 )
645 {
646
647 StringBuilder builder = new StringBuilder();
648 builder.append( parts[1] );
649 for ( int i = 2; i <= parts.length - 2; i++ )
650 {
651 builder.append( "-" );
652 builder.append( parts[i] );
653 }
654 return builder.toString();
655 }
656 return null;
657 }
658
659 @Override
660 protected void preExecute( Executor exec, CommandLine commandLine, Properties enviro )
661 throws MojoExecutionException
662 {
663 HashMap<String, String> dependenciesRoots = new HashMap<String, String>();
664
665
666
667 if ( null != additionalDependenciesRoots )
668 {
669 for ( String dependencyRoot : additionalDependenciesRoots )
670 {
671
672 String cur = dependencyRoot + "/" + targetClassifier + "/"
673 + buildConfig;
674 dependenciesRoots.put( cur, cur );
675 getLog().info( "add additional Dependency Root: \"" + cur + "\"" );
676 }
677 }
678
679
680
681 @SuppressWarnings( "unchecked" ) Set<Artifact> dependencyArtifacts = project.getDependencyArtifacts();
682 Iterator<Artifact> itDeps = dependencyArtifacts.iterator();
683 while ( itDeps.hasNext() )
684 {
685 Artifact cur = itDeps.next();
686 String artifactId = cur.getArtifactId();
687 String classifer = cur.getClassifier();
688 if ( !StringUtils.isEmpty( classifer ) && 0 == classifer.indexOf( "bin" ) )
689 {
690 String artifactBuildConfig = extractBuildConfig( classifer );
691 String artifactBuildConfigGeneralized = artifactBuildConfig;
692 if ( StringUtils.isEmpty( artifactBuildConfig ) || artifactBuildConfig.equals( buildConfig ) )
693 {
694 artifactBuildConfig = buildConfig;
695 artifactBuildConfigGeneralized = buildConfig;
696 }
697
698 String artifactSubClassifier = extractSubClassifier( classifer );
699 String artifactSubClassifierGeneralized = artifactSubClassifier;
700 if ( StringUtils.isEmpty( artifactSubClassifier )
701 || artifactSubClassifier.equals( targetClassifier ) )
702 {
703 artifactSubClassifier = targetClassifier;
704 artifactSubClassifierGeneralized = "${TARGET_CLASSIFIER}";
705 }
706
707 String newDepRoot = getProjectDependenciesDirectory() + "/"
708 + artifactSubClassifier + "/" + artifactBuildConfig + "/" + artifactId;
709
710 String newDepRootGeneralized = "${DEPENDENCY_DIR}" + "/"
711 + artifactSubClassifierGeneralized + "/" + artifactBuildConfigGeneralized
712 + "/" + artifactId;
713
714 if ( !dependenciesRoots.containsKey( newDepRoot ) )
715 {
716 dependenciesRoots.put( newDepRoot, newDepRootGeneralized );
717 getLog().info( "add Dependency Root: \"" + newDepRoot + "\" <=> \""
718 + newDepRootGeneralized + "\"" );
719 }
720 }
721 }
722
723 ArrayList<String> dependenciesLib = new ArrayList<String>();
724 findDependencies( dependenciesRoots, dependenciesLib );
725
726 updateOrCreateCMakeDependenciesFile( dependenciesLib, true );
727 updateOrCreateCMakeDependenciesFile( dependenciesLib, false );
728 }
729 }