2929import java .io .PrintStream ;
3030import java .nio .charset .Charset ;
3131import java .nio .file .Files ;
32+ import java .nio .file .Path ;
33+ import java .nio .file .Paths ;
3234import java .util .ArrayList ;
3335import java .util .Collections ;
3436import java .util .HashSet ;
3537import java .util .LinkedHashMap ;
3638import java .util .List ;
39+ import java .util .ListIterator ;
3740import java .util .Map ;
3841import java .util .Map .Entry ;
3942import java .util .Properties ;
102105import org .codehaus .plexus .classworlds .realm .ClassRealm ;
103106import org .codehaus .plexus .classworlds .realm .NoSuchRealmException ;
104107import org .codehaus .plexus .component .repository .exception .ComponentLookupException ;
108+ import org .codehaus .plexus .interpolation .AbstractValueSource ;
109+ import org .codehaus .plexus .interpolation .InterpolationException ;
110+ import org .codehaus .plexus .interpolation .StringSearchInterpolator ;
105111import org .codehaus .plexus .logging .LoggerManager ;
106112import org .codehaus .plexus .util .StringUtils ;
107113import org .codehaus .plexus .util .xml .pull .XmlPullParserException ;
@@ -140,9 +146,14 @@ public class MavenCli {
140146
141147 private static final String EXT_CLASS_PATH = "maven.ext.class.path" ;
142148
143- private static final String EXTENSIONS_FILENAME = ".mvn/extensions.xml " ;
149+ private static final String DOT_MVN = ".mvn" ;
144150
145- private static final String MVN_MAVEN_CONFIG = ".mvn/maven.config" ;
151+ private static final String UNABLE_TO_FIND_ROOT_PROJECT_MESSAGE = "Unable to find the root directory. Create a "
152+ + DOT_MVN + " directory in the project root directory to identify it." ;
153+
154+ private static final String EXTENSIONS_FILENAME = DOT_MVN + "/extensions.xml" ;
155+
156+ private static final String MVN_MAVEN_CONFIG = DOT_MVN + "/maven.config" ;
146157
147158 public static final String STYLE_COLOR_PROPERTY = "style.color" ;
148159
@@ -309,6 +320,47 @@ void initialize(CliRequest cliRequest) throws ExitException {
309320 }
310321 }
311322
323+ // We need to locate the top level project which may be pointed at using
324+ // the -f/--file option. However, the command line isn't parsed yet, so
325+ // we need to iterate through the args to find it and act upon it.
326+ Path topDirectory = Paths .get (cliRequest .workingDirectory );
327+ boolean isAltFile = false ;
328+ for (String arg : cliRequest .args ) {
329+ if (isAltFile ) {
330+ // this is the argument following -f/--file
331+ Path path = topDirectory .resolve (arg );
332+ if (Files .isDirectory (path )) {
333+ topDirectory = path ;
334+ } else if (Files .isRegularFile (path )) {
335+ topDirectory = path .getParent ();
336+ if (!Files .isDirectory (topDirectory )) {
337+ System .err .println ("Directory " + topDirectory
338+ + " extracted from the -f/--file command-line argument " + arg + " does not exist" );
339+ throw new ExitException (1 );
340+ }
341+ } else {
342+ System .err .println (
343+ "POM file " + arg + " specified with the -f/--file command line argument does not exist" );
344+ throw new ExitException (1 );
345+ }
346+ break ;
347+ } else {
348+ // Check if this is the -f/--file option
349+ isAltFile = arg .equals (String .valueOf (CLIManager .ALTERNATE_POM_FILE )) || arg .equals ("file" );
350+ }
351+ }
352+ try {
353+ topDirectory = topDirectory .toAbsolutePath ().toRealPath ();
354+ } catch (IOException e ) {
355+ System .err .println ("Error computing real path from " + topDirectory + ": " + e .getMessage ());
356+ throw new ExitException (1 );
357+ }
358+ cliRequest .topDirectory = topDirectory ;
359+ // We're very early in the process and we don't have the container set up yet,
360+ // so we on searchAcceptableRootDirectory method to find us acceptable directory.
361+ // The method may return null if nothing acceptable found.
362+ cliRequest .rootDirectory = searchAcceptableRootDirectory (topDirectory );
363+
312364 //
313365 // Make sure the Maven home directory is an absolute path to save us from confusion with say drive-relative
314366 // Windows paths.
@@ -526,8 +578,39 @@ private void commands(CliRequest cliRequest) {
526578
527579 // Needed to make this method package visible to make writing a unit test possible
528580 // Maybe it's better to move some of those methods to separate class (SoC).
529- void properties (CliRequest cliRequest ) {
530- populateProperties (cliRequest .commandLine , cliRequest .systemProperties , cliRequest .userProperties );
581+ void properties (CliRequest cliRequest ) throws ExitException {
582+ try {
583+ populateProperties (cliRequest , cliRequest .systemProperties , cliRequest .userProperties );
584+
585+ StringSearchInterpolator interpolator =
586+ createInterpolator (cliRequest , cliRequest .systemProperties , cliRequest .userProperties );
587+ CommandLine .Builder commandLineBuilder = new CommandLine .Builder ();
588+ for (Option option : cliRequest .commandLine .getOptions ()) {
589+ if (!String .valueOf (CLIManager .SET_USER_PROPERTY ).equals (option .getOpt ())) {
590+ List <String > values = option .getValuesList ();
591+ for (ListIterator <String > it = values .listIterator (); it .hasNext (); ) {
592+ it .set (interpolator .interpolate (it .next ()));
593+ }
594+ }
595+ commandLineBuilder .addOption (option );
596+ }
597+ for (String arg : cliRequest .commandLine .getArgList ()) {
598+ commandLineBuilder .addArg (interpolator .interpolate (arg ));
599+ }
600+ cliRequest .commandLine = commandLineBuilder .build ();
601+ } catch (InterpolationException e ) {
602+ String message = "ERROR: Could not interpolate properties and/or arguments: " + e .getMessage ();
603+ System .err .println (message );
604+ throw new ExitException (1 ); // user error
605+ } catch (IllegalUseOfUndefinedProperty e ) {
606+ String message = "ERROR: Illegal use of undefined property: " + e .property ;
607+ System .err .println (message );
608+ if (cliRequest .rootDirectory == null ) {
609+ System .err .println ();
610+ System .err .println (UNABLE_TO_FIND_ROOT_PROJECT_MESSAGE );
611+ }
612+ throw new ExitException (1 ); // user error
613+ }
531614 }
532615
533616 PlexusContainer container (CliRequest cliRequest ) throws Exception {
@@ -1405,27 +1488,54 @@ int calculateDegreeOfConcurrency(String threadConfiguration) {
14051488 // Properties handling
14061489 // ----------------------------------------------------------------------
14071490
1408- static void populateProperties (CommandLine commandLine , Properties systemProperties , Properties userProperties ) {
1409- EnvironmentUtils . addEnvVars ( systemProperties );
1491+ static void populateProperties (CliRequest cliRequest , Properties systemProperties , Properties userProperties )
1492+ throws InterpolationException {
14101493
14111494 // ----------------------------------------------------------------------
14121495 // Options that are set on the command line become system properties
14131496 // and therefore are set in the session properties. System properties
14141497 // are most dominant.
14151498 // ----------------------------------------------------------------------
14161499
1417- if (commandLine .hasOption (CLIManager .SET_USER_PROPERTY )) {
1418- String [] defStrs = commandLine .getOptionValues (CLIManager .SET_USER_PROPERTY );
1500+ Properties cliProperties = new Properties ();
1501+ if (cliRequest .commandLine .hasOption (CLIManager .SET_USER_PROPERTY )) {
1502+ String [] defStrs = cliRequest .commandLine .getOptionValues (CLIManager .SET_USER_PROPERTY );
14191503
14201504 if (defStrs != null ) {
1421- for (String defStr : defStrs ) {
1422- setCliProperty (defStr , userProperties );
1505+ String name ;
1506+ String value ;
1507+ for (String property : defStrs ) {
1508+ int i = property .indexOf ('=' );
1509+ if (i <= 0 ) {
1510+ name = property .trim ();
1511+ value = "true" ;
1512+ } else {
1513+ name = property .substring (0 , i ).trim ();
1514+ value = property .substring (i + 1 );
1515+ }
1516+ cliProperties .setProperty (name , value );
14231517 }
14241518 }
14251519 }
14261520
1521+ EnvironmentUtils .addEnvVars (systemProperties );
14271522 SystemProperties .addSystemProperties (systemProperties );
14281523
1524+ StringSearchInterpolator interpolator = createInterpolator (cliRequest , cliProperties , systemProperties );
1525+ for (Map .Entry <Object , Object > e : cliProperties .entrySet ()) {
1526+ String name = (String ) e .getKey ();
1527+ String value = interpolator .interpolate ((String ) e .getValue ());
1528+ userProperties .setProperty (name , value );
1529+ }
1530+
1531+ systemProperties .putAll (userProperties );
1532+
1533+ // ----------------------------------------------------------------------
1534+ // I'm leaving the setting of system properties here as not to break
1535+ // the SystemPropertyProfileActivator. This won't harm embedding. jvz.
1536+ // ----------------------------------------------------------------------
1537+ userProperties .forEach ((k , v ) -> System .setProperty ((String ) k , (String ) v ));
1538+
14291539 // ----------------------------------------------------------------------
14301540 // Properties containing info about the currently running version of Maven
14311541 // These override any corresponding properties set on the command line
@@ -1440,31 +1550,56 @@ static void populateProperties(CommandLine commandLine, Properties systemPropert
14401550 systemProperties .setProperty ("maven.build.version" , mavenBuildVersion );
14411551 }
14421552
1443- private static void setCliProperty (String property , Properties properties ) {
1444- String name ;
1445-
1446- String value ;
1447-
1448- int i = property .indexOf ('=' );
1449-
1450- if (i <= 0 ) {
1451- name = property .trim ();
1452-
1453- value = "true" ;
1454- } else {
1455- name = property .substring (0 , i ).trim ();
1553+ protected boolean isAcceptableRootDirectory (Path path ) {
1554+ return path != null && Files .isDirectory (path .resolve (DOT_MVN ));
1555+ }
14561556
1457- value = property .substring (i + 1 );
1557+ protected Path searchAcceptableRootDirectory (Path path ) {
1558+ if (path == null ) {
1559+ return null ;
14581560 }
1561+ if (isAcceptableRootDirectory (path )) {
1562+ return path ;
1563+ }
1564+ return searchAcceptableRootDirectory (path .getParent ());
1565+ }
14591566
1460- properties .setProperty (name , value );
1461-
1462- // ----------------------------------------------------------------------
1463- // I'm leaving the setting of system properties here as not to break
1464- // the SystemPropertyProfileActivator. This won't harm embedding. jvz.
1465- // ----------------------------------------------------------------------
1466-
1467- System .setProperty (name , value );
1567+ protected static StringSearchInterpolator createInterpolator (CliRequest cliRequest , Properties ... properties ) {
1568+ StringSearchInterpolator interpolator = new StringSearchInterpolator ();
1569+ interpolator .addValueSource (new AbstractValueSource (false ) {
1570+ @ Override
1571+ public Object getValue (String expression ) {
1572+ if ("session.topDirectory" .equals (expression )) {
1573+ Path topDirectory = cliRequest .topDirectory ;
1574+ if (topDirectory != null ) {
1575+ return topDirectory .toString ();
1576+ } else {
1577+ throw new IllegalUseOfUndefinedProperty (expression );
1578+ }
1579+ } else if ("session.rootDirectory" .equals (expression )) {
1580+ Path rootDirectory = cliRequest .rootDirectory ;
1581+ if (rootDirectory != null ) {
1582+ return rootDirectory .toString ();
1583+ } else {
1584+ throw new IllegalUseOfUndefinedProperty (expression );
1585+ }
1586+ }
1587+ return null ;
1588+ }
1589+ });
1590+ interpolator .addValueSource (new AbstractValueSource (false ) {
1591+ @ Override
1592+ public Object getValue (String expression ) {
1593+ for (Properties props : properties ) {
1594+ Object val = props .getProperty (expression );
1595+ if (val != null ) {
1596+ return val ;
1597+ }
1598+ }
1599+ return null ;
1600+ }
1601+ });
1602+ return interpolator ;
14681603 }
14691604
14701605 static class ExitException extends Exception {
@@ -1475,6 +1610,14 @@ static class ExitException extends Exception {
14751610 }
14761611 }
14771612
1613+ static class IllegalUseOfUndefinedProperty extends IllegalArgumentException {
1614+ final String property ;
1615+
1616+ IllegalUseOfUndefinedProperty (String property ) {
1617+ this .property = property ;
1618+ }
1619+ }
1620+
14781621 //
14791622 // Customizations available via the CLI
14801623 //
0 commit comments