@@ -7,6 +7,7 @@ import java.util.jar.Attributes.Name
7
7
import scala .tools .asm .ClassReader
8
8
import scala .tools .asm .tree .ClassNode
9
9
import dotty .tools .io .*
10
+ import dotty .tools .dotc .core .Decorators .*
10
11
import dotty .tools .dotc .util .NoSourcePosition
11
12
import java .nio .charset .StandardCharsets
12
13
import java .nio .channels .ClosedByInterruptException
@@ -15,36 +16,39 @@ import scala.language.unsafeNulls
15
16
16
17
class ClassfileWriter (frontendAccess : PostProcessorFrontendAccess ) {
17
18
import frontendAccess .{backendReporting , compilerSettings }
18
-
19
+
19
20
// if non-null, classfiles are additionally written to this directory
20
21
private val dumpOutputDir : AbstractFile = getDirectoryOrNull(compilerSettings.dumpClassesDirectory)
21
22
22
23
// if non-null, classfiles are written to a jar instead of the output directory
23
- private val jarWriter : JarWriter =
24
- val f = compilerSettings.outputDirectory
25
- if f.hasExtension(" jar" ) then
26
- // If no main class was specified, see if there's only one
27
- // entry point among the classes going into the jar.
28
- val mainClass = compilerSettings.mainClass match {
29
- case c @ Some (m) =>
30
- backendReporting.log(s " Main-Class was specified: ${m}" )
31
- c
32
- case None => frontendAccess.getEntryPoints match {
33
- case Nil =>
34
- backendReporting.log(" No Main-Class designated or discovered." )
35
- None
24
+ private val jarWriter : JarWriter | Null = compilerSettings.outputDirectory match {
25
+ case jar : JarArchive =>
26
+ val mainClass = compilerSettings.mainClass.orElse {
27
+ // If no main class was specified, see if there's only one
28
+ // entry point among the classes going into the jar.
29
+ frontendAccess.getEntryPoints match {
36
30
case name :: Nil =>
37
- backendReporting.log(s " Unique entry point: setting Main-Class to $name" )
31
+ backendReporting.log(i " Unique entry point: setting Main-Class to $name" )
38
32
Some (name)
39
33
case names =>
40
- backendReporting.log(s " No Main-Class due to multiple entry points: \n ${names.mkString(" \n " )}" )
34
+ if names.isEmpty then backendReporting.warning(em " No Main-Class designated or discovered. " )
35
+ else backendReporting.warning(em " No Main-Class due to multiple entry points: \n ${names.mkString(" \n " )}" )
41
36
None
42
37
}
43
38
}
39
+ jar.underlyingSource.map{ source =>
40
+ if jar.isEmpty then
41
+ val jarMainAttrs = mainClass.map(Name .MAIN_CLASS -> _).toList
42
+ new Jar (source.file).jarWriter(jarMainAttrs : _* )
43
+ else
44
+ // Writing to non-empty JAR might be an undefined behaviour, e.g. in case if other files where
45
+ // created using `AbstractFile.bufferedOutputStream`instead of JarWritter
46
+ backendReporting.warning(em " Tried to write to non-empty JAR: $source" )
47
+ null
48
+ }.orNull
44
49
45
- val jarMainAttrs = mainClass.map(c => Name .MAIN_CLASS -> c).toList
46
- new Jar (f.file).jarWriter(jarMainAttrs : _* )
47
- else null
50
+ case _ => null
51
+ }
48
52
49
53
private def getDirectoryOrNull (dir : Option [String ]): AbstractFile =
50
54
dir.map(d => new PlainDirectory (Directory (d))).orNull
@@ -88,20 +92,9 @@ class ClassfileWriter(frontendAccess: PostProcessorFrontendAccess) {
88
92
}
89
93
}
90
94
91
- def write (className : InternalName , bytes : Array [Byte ], sourceFile : AbstractFile ): AbstractFile | Null = try {
95
+ def writeClass (className : InternalName , bytes : Array [Byte ], sourceFile : AbstractFile ): AbstractFile | Null = try {
92
96
// val writeStart = Statistics.startTimer(BackendStats.bcodeWriteTimer)
93
- val outFile = if (jarWriter == null ) {
94
- val outFolder = compilerSettings.outputDirectory
95
- val outFile = getFile(outFolder, className, " .class" )
96
- writeBytes(outFile, bytes)
97
- outFile
98
- } else {
99
- val path = className + " .class"
100
- val out = jarWriter.newOutputStream(path)
101
- try out.write(bytes, 0 , bytes.length)
102
- finally out.flush()
103
- null
104
- }
97
+ val outFile = writeToJarOrFile(className, bytes, " .class" )
105
98
// Statistics.stopTimer(BackendStats.bcodeWriteTimer, writeStart)
106
99
107
100
if (dumpOutputDir != null ) {
@@ -111,27 +104,39 @@ class ClassfileWriter(frontendAccess: PostProcessorFrontendAccess) {
111
104
outFile
112
105
} catch {
113
106
case e : FileConflictException =>
114
- backendReporting.error(s " error writing $className: ${e.getMessage}" , NoSourcePosition )
107
+ backendReporting.error(em " error writing $className: ${e.getMessage}" )
115
108
null
116
109
case e : java.nio.file.FileSystemException =>
117
110
if compilerSettings.debug then e.printStackTrace()
118
- backendReporting.error(s " error writing $className: ${e.getClass.getName} ${e.getMessage}" , NoSourcePosition )
111
+ backendReporting.error(em " error writing $className: ${e.getClass.getName} ${e.getMessage}" )
119
112
null
120
113
}
121
114
122
- def writeTasty (className : InternalName , bytes : Array [Byte ]): Unit =
123
- val outFolder = compilerSettings.outputDirectory
124
- val outFile = getFile(outFolder, className, " .tasty" )
125
- try writeBytes(outFile, bytes)
126
- catch case ex : ClosedByInterruptException =>
127
- try outFile.delete() // don't leave an empty or half-written tastyfile around after an interrupt
128
- catch case _ : Throwable => ()
129
- finally throw ex
115
+ def writeTasty (className : InternalName , bytes : Array [Byte ]): Unit =
116
+ writeToJarOrFile(className, bytes, " .tasty" )
117
+
118
+ private def writeToJarOrFile (className : InternalName , bytes : Array [Byte ], suffix : String ): AbstractFile | Null = {
119
+ if jarWriter == null then
120
+ val outFolder = compilerSettings.outputDirectory
121
+ val outFile = getFile(outFolder, className, suffix)
122
+ try writeBytes(outFile, bytes)
123
+ catch case ex : ClosedByInterruptException =>
124
+ try outFile.delete() // don't leave an empty or half-written files around after an interrupt
125
+ catch case _ : Throwable => ()
126
+ finally throw ex
127
+ outFile
128
+ else
129
+ val path = className + suffix
130
+ val out = jarWriter.newOutputStream(path)
131
+ try out.write(bytes, 0 , bytes.length)
132
+ finally out.flush()
133
+ null
134
+ }
130
135
131
136
def close (): Unit = {
132
137
if (jarWriter != null ) jarWriter.close()
133
138
}
134
139
}
135
140
136
141
/** Can't output a file due to the state of the file system. */
137
- class FileConflictException (msg : String , val file : AbstractFile ) extends IOException (msg)
142
+ class FileConflictException (msg : String , val file : AbstractFile ) extends IOException (msg)
0 commit comments