36
36
import org .enso .distribution .Environment ;
37
37
import org .enso .editions .DefaultEdition ;
38
38
import org .enso .libraryupload .LibraryUploader .UploadFailedError ;
39
- import org .enso .os .environment .chdir .WorkingDirectory ;
40
39
import org .enso .pkg .Contact ;
41
40
import org .enso .pkg .PackageManager ;
42
41
import org .enso .pkg .PackageManager$ ;
51
50
import org .enso .runner .common .WrongOption ;
52
51
import org .enso .version .BuildVersion ;
53
52
import org .enso .version .VersionDescription ;
54
- import org .graalvm .nativeimage .ImageInfo ;
55
53
import org .graalvm .polyglot .PolyglotException ;
56
54
import org .graalvm .polyglot .PolyglotException .StackFrame ;
57
55
import org .graalvm .polyglot .SourceSection ;
58
56
import org .graalvm .polyglot .io .MessageTransport ;
57
+ import org .slf4j .Logger ;
59
58
import org .slf4j .LoggerFactory ;
60
59
import org .slf4j .MDC ;
61
60
import org .slf4j .event .Level ;
@@ -110,7 +109,7 @@ public class Main {
110
109
111
110
private static final String DEFAULT_MAIN_METHOD_NAME = "main" ;
112
111
113
- private static final org . slf4j . Logger logger = LoggerFactory .getLogger (Main .class );
112
+ private static final Logger LOGGER = LoggerFactory .getLogger (Main .class );
114
113
115
114
Main () {}
116
115
@@ -611,9 +610,9 @@ private void createNew(
611
610
: join (new Contact (authorName , authorEmail ), nil ());
612
611
613
612
var edition = DefaultEdition .getDefaultEdition ();
614
- if (logger .isTraceEnabled ()) {
613
+ if (LOGGER .isTraceEnabled ()) {
615
614
var baseEdition = edition .parent ().getOrElse (() -> "<no-base>" );
616
- logger .trace ("Creating a new project " + name + " based on edition [" + baseEdition + "]." );
615
+ LOGGER .trace ("Creating a new project " + name + " based on edition [" + baseEdition + "]." );
617
616
}
618
617
619
618
var template =
@@ -659,6 +658,7 @@ private void createNew(
659
658
* @param logMasking whether or not log masking is enabled
660
659
*/
661
660
private void compile (
661
+ String cwd ,
662
662
String path ,
663
663
boolean shouldCompileDependencies ,
664
664
boolean shouldUseGlobalCache ,
@@ -669,10 +669,8 @@ private void compile(
669
669
Level logLevel ,
670
670
boolean logMasking )
671
671
throws IOException {
672
- var fileAndProject = Utils .findFileAndProject (path , null );
673
- if (fileAndProject == null ) {
674
- throw exitFail ("No package exists at " + path + "." );
675
- }
672
+ var fileAndProject = Utils .findFileAndProject (cwd , path , null );
673
+ assert fileAndProject != null ;
676
674
677
675
boolean isProjectMode = fileAndProject ._1 ();
678
676
String projectPath = fileAndProject ._3 ();
@@ -708,7 +706,7 @@ private void compile(
708
706
throw exitFail ("Compilation failed due to " + reason + "." );
709
707
} else {
710
708
String message = "Unexpected internal error: " + t .getMessage ();
711
- logger .error (message , t );
709
+ LOGGER .error (message , t );
712
710
throw exitFail (message );
713
711
}
714
712
@@ -737,6 +735,7 @@ private void compile(
737
735
* null}
738
736
*/
739
737
private void handleRun (
738
+ String cwd ,
740
739
String path ,
741
740
List <String > additionalArgs ,
742
741
String projectPath ,
@@ -752,10 +751,8 @@ private void handleRun(
752
751
String executionEnvironment ,
753
752
int warningsLimit )
754
753
throws IOException {
755
- var fileAndProject = Utils .findFileAndProject (path , projectPath );
756
- if (fileAndProject == null ) {
757
- throw exitFail ("Cannot find " + path + " and " + projectPath );
758
- }
754
+ var fileAndProject = Utils .findFileAndProject (cwd , path , projectPath );
755
+ assert fileAndProject != null ;
759
756
var projectMode = fileAndProject ._1 ();
760
757
var file = fileAndProject ._2 ();
761
758
var mainFile = file ;
@@ -820,7 +817,7 @@ private void handleRun(
820
817
} catch (RuntimeException e ) {
821
818
// forces computation of the exception message sooner than context is closed
822
819
// should work around issues seen at #11127
823
- logger .debug ("Execution failed with " + e .getMessage ());
820
+ LOGGER .debug ("Execution failed with " + e .getMessage ());
824
821
throw e ;
825
822
} finally {
826
823
context .context ().close ();
@@ -840,24 +837,23 @@ private void handleRun(
840
837
*/
841
838
private void genDocs (
842
839
String docsFormat ,
840
+ String cwd ,
843
841
String projectPath ,
844
842
Level logLevel ,
845
843
boolean logMasking ,
846
- boolean enableIrCaches ) {
844
+ boolean enableIrCaches )
845
+ throws IOException {
847
846
if (projectPath == null || projectPath .isEmpty ()) {
848
847
throw exitFail ("Specify path to a project with --in-project option" );
849
848
}
850
- if (!fileExists (projectPath )) {
849
+ var fileAndProject = Utils .findFileAndProject (cwd , projectPath , null );
850
+ if (fileAndProject == null ) {
851
851
throw exitFail ("Project specified in --in-project option does not exist: " + projectPath );
852
852
}
853
853
generateDocsFrom (docsFormat , projectPath , logLevel , logMasking , enableIrCaches );
854
854
throw exitSuccess ();
855
855
}
856
856
857
- private static boolean fileExists (String path ) {
858
- return new File (path ).exists ();
859
- }
860
-
861
857
/**
862
858
* Subroutine of `genDocs` function. Generates the documentation for given Enso project at given
863
859
* path.
@@ -901,7 +897,7 @@ private void preinstallDependencies(String projectPath, Level logLevel) {
901
897
DependencyPreinstaller .preinstallDependencies (new File (projectPath ), logLevel );
902
898
throw exitSuccess ();
903
899
} catch (RuntimeException error ) {
904
- logger .error ("Dependency installation failed: " + error .getMessage (), error );
900
+ LOGGER .error ("Dependency installation failed: " + error .getMessage (), error );
905
901
throw exitFail ("Dependency installation failed: " + error .getMessage ());
906
902
}
907
903
}
@@ -960,7 +956,7 @@ private void runMain(
960
956
listOfArgs = join (e , listOfArgs );
961
957
}
962
958
listOfArgs = listOfArgs .reverse ();
963
- logger .debug ("Executing the main function with arguments {}" , listOfArgs .mkString (", " ));
959
+ LOGGER .debug ("Executing the main function with arguments {}" , listOfArgs .mkString (", " ));
964
960
var res = main .execute (listOfArgs );
965
961
if (!res .isNull ()) {
966
962
var textRes = res .isString () ? res .asString () : res .toString ();
@@ -1045,7 +1041,7 @@ private static MessageTransport replTransport() {
1045
1041
var repl = futureRepl .get ();
1046
1042
return new DebuggerSessionManagerEndpoint (repl , peer );
1047
1043
} catch (InterruptedException | ExecutionException ex ) {
1048
- logger .error ("Cannot initialize REPL transport" , ex );
1044
+ LOGGER .error ("Cannot initialize REPL transport" , ex );
1049
1045
}
1050
1046
}
1051
1047
return null ;
@@ -1105,11 +1101,13 @@ public static void main(String[] args) throws Exception {
1105
1101
/**
1106
1102
* Main entry point for the CLI program.
1107
1103
*
1104
+ * @param cwd current working directory to use
1108
1105
* @param line the provided command line arguments
1109
1106
* @param logLevel the provided log level
1110
1107
* @param logMasking the flag indicating if the log masking is enabled
1111
1108
*/
1112
- final void mainEntry (CommandLine line , Level logLevel , boolean logMasking ) throws IOException {
1109
+ final void mainEntry (String cwd , CommandLine line , Level logLevel , boolean logMasking )
1110
+ throws IOException {
1113
1111
if (line .hasOption (HELP_OPTION )) {
1114
1112
printHelp ();
1115
1113
throw exitSuccess ();
@@ -1180,6 +1178,7 @@ final void mainEntry(CommandLine line, Level logLevel, boolean logMasking) throw
1180
1178
var shouldUseGlobalCache = !line .hasOption (NO_GLOBAL_CACHE_OPTION );
1181
1179
1182
1180
compile (
1181
+ cwd ,
1183
1182
packagePath ,
1184
1183
shouldCompileDependencies ,
1185
1184
shouldUseGlobalCache ,
@@ -1191,8 +1190,10 @@ final void mainEntry(CommandLine line, Level logLevel, boolean logMasking) throw
1191
1190
logMasking );
1192
1191
}
1193
1192
1193
+ LOGGER .debug ("Original working directory={}, cwd={}" , cwd , System .getProperty ("user.dir" ));
1194
1194
if (line .hasOption (RUN_OPTION )) {
1195
1195
handleRun (
1196
+ cwd ,
1196
1197
line .getOptionValue (RUN_OPTION ),
1197
1198
Arrays .asList (line .getArgs ()),
1198
1199
line .getOptionValue (IN_PROJECT_OPTION ),
@@ -1222,6 +1223,7 @@ final void mainEntry(CommandLine line, Level logLevel, boolean logMasking) throw
1222
1223
if (line .hasOption (DOCS_OPTION )) {
1223
1224
genDocs (
1224
1225
line .getOptionValue (DOCS_OPTION ),
1226
+ cwd ,
1225
1227
line .getOptionValue (IN_PROJECT_OPTION ),
1226
1228
logLevel ,
1227
1229
logMasking ,
@@ -1296,7 +1298,7 @@ private static <A> A withProfiling(
1296
1298
try {
1297
1299
sampler .stop ();
1298
1300
} catch (IOException ex ) {
1299
- logger .error ("Error stopping sampler" , ex );
1301
+ LOGGER .error ("Error stopping sampler" , ex );
1300
1302
}
1301
1303
return BoxedUnit .UNIT ;
1302
1304
});
@@ -1504,8 +1506,11 @@ private void launchJvm(
1504
1506
private void launch (String [] args ) throws IOException , InterruptedException , URISyntaxException {
1505
1507
var line = preprocessArguments (args );
1506
1508
1507
- if (line .hasOption (RUN_OPTION )) {
1508
- maybeChangeWorkingDirToProjectRoot (line .getOptionValue (RUN_OPTION ));
1509
+ String originalCwdOrNull = null ;
1510
+ if (line .hasOption (IN_PROJECT_OPTION )) {
1511
+ originalCwdOrNull = Utils .adjustCwdToProject (line .getOptionValue (IN_PROJECT_OPTION ));
1512
+ } else if (line .hasOption (RUN_OPTION )) {
1513
+ originalCwdOrNull = Utils .adjustCwdToProject (line .getOptionValue (RUN_OPTION ));
1509
1514
}
1510
1515
1511
1516
var logMasking = new boolean [1 ];
@@ -1556,15 +1561,15 @@ private void launch(String[] args) throws IOException, InterruptedException, URI
1556
1561
}
1557
1562
}
1558
1563
1559
- launch ( line , logLevel , logMasking [0 ]);
1564
+ handleLaunch ( originalCwdOrNull , line , logLevel , logMasking [0 ]);
1560
1565
}
1561
1566
1562
1567
final CommandLine preprocessArguments (String ... args ) {
1563
1568
var parser = new DefaultParser ();
1564
1569
try {
1565
1570
var startParsing = System .currentTimeMillis ();
1566
1571
var line = parser .parse (CLI_OPTIONS , args );
1567
- logger .trace (
1572
+ LOGGER .trace (
1568
1573
"Parsing Language Server arguments took {0}ms" ,
1569
1574
System .currentTimeMillis () - startParsing );
1570
1575
return line ;
@@ -1574,41 +1579,6 @@ final CommandLine preprocessArguments(String... args) {
1574
1579
}
1575
1580
}
1576
1581
1577
- /**
1578
- * This method has to be called as early as possible. It attempts to find the project root
1579
- * directory of the given file, and if the project root is found, it uses native code to change
1580
- * the working directory to the project root. In order for the JVM's {@code java.io} to reflect
1581
- * the working directory change, this methods must be called before any class from {@code java.io}
1582
- * is accessed.
1583
- *
1584
- * <p>Note that invoking native code is the only reliable way to change the working directory in
1585
- * the current process.
1586
- *
1587
- * <p>For detailed explanation see this <a
1588
- * href="https://github.com/enso-org/enso/pull/12618#issuecomment-2778451448">GH comment</a>.
1589
- *
1590
- * @param fileToRun the file to run, value of the {@code --run} option.
1591
- */
1592
- private void maybeChangeWorkingDirToProjectRoot (String fileToRun ) {
1593
- assert fileToRun != null ;
1594
- if (!ImageInfo .inImageRuntimeCode ()) {
1595
- return ;
1596
- }
1597
- var nativeApi = WorkingDirectory .getInstance ();
1598
- var projectRoot = nativeApi .findProjectRoot (fileToRun );
1599
- if (projectRoot != null ) {
1600
- var parentDir = nativeApi .parentFile (projectRoot );
1601
- assert parentDir != null ;
1602
- var curDir = nativeApi .currentWorkingDir ();
1603
- if (!parentDir .equals (curDir )) {
1604
- var dirChanged = nativeApi .changeWorkingDir (parentDir );
1605
- if (!dirChanged ) {
1606
- logger .error ("Cannot change working directory to {}" , parentDir );
1607
- }
1608
- }
1609
- }
1610
- }
1611
-
1612
1582
private Level setupLogging (CommandLine line , boolean [] logMasking ) {
1613
1583
var logLevel =
1614
1584
scala .Option .apply (line .getOptionValue (LOG_LEVEL ))
@@ -1638,7 +1608,8 @@ private Level setupLogging(CommandLine line, boolean[] logMasking) {
1638
1608
return logLevel ;
1639
1609
}
1640
1610
1641
- private void launch (CommandLine line , Level logLevel , boolean logMasking ) {
1611
+ private final void handleLaunch (
1612
+ String cwd , CommandLine line , Level logLevel , boolean logMasking ) {
1642
1613
if (line .hasOption (LANGUAGE_SERVER_OPTION )) {
1643
1614
try {
1644
1615
var conf = parseProfilingConfig (line );
@@ -1663,12 +1634,14 @@ private void launch(CommandLine line, Level logLevel, boolean logMasking) {
1663
1634
conf ,
1664
1635
ExecutionContext .global (),
1665
1636
() -> {
1666
- mainEntry (line , logLevel , logMasking );
1637
+ mainEntry (cwd , line , logLevel , logMasking );
1667
1638
return BoxedUnit .UNIT ;
1668
1639
});
1640
+ } catch (ExitCode ex ) {
1641
+ throw exitFail (ex .getMessage ());
1669
1642
} catch (IOException ex ) {
1670
- if (logger .isDebugEnabled ()) {
1671
- logger .error ("Error during execution" , ex );
1643
+ if (LOGGER .isDebugEnabled ()) {
1644
+ LOGGER .error ("Error during execution" , ex );
1672
1645
}
1673
1646
throw exitFail ("Command failed with an error: " + ex .getMessage ());
1674
1647
}
0 commit comments