Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions examples/package-example/Main.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package package_example
object Main{
def main( args: Array[String] ): Unit = {
println( Console.GREEN ++ "Hello World" ++ Console.RESET )
}
}
42 changes: 42 additions & 0 deletions examples/package-example/build/build.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package package_example_build
import java.nio.file._
import java.security.MessageDigest
import cbt._
class Build(val context: Context) extends BaseBuild with PackageJars {
override def groupId = "org.example"
override def version = "1.0.0"

def hash(file: Path): String = {
val digest = MessageDigest.getInstance("SHA-256");
val in = Files.newInputStream(file)
try {
val chunk = new Array[Byte](4096)
while(in.read(chunk) > 0) {
digest.update(chunk)
}
} finally {
in.close
}
val bytes = digest.digest()
val builder = new StringBuilder(bytes.length * 2);
for(b <- bytes) {
builder.append(f"$b%02x")
}
builder.toString
}

override def run = {
val pass1 = super.`package`.map(f => hash(f.toPath))
lib.clean(cleanFiles, true, false, false, false)
transientCache.clear()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This cache invalidation is required here, or else the second pass will fail due to missing files. Should this be moved to the clean helper method in lib?

Also, I'm not sure if I have the same concept of what a clean operation should do, but IMO there should not be the need to prompt the user for a confirmation since we're only deleting generated files.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would probably be safer if we don't try to clear caches from within a single cbt run.

What if in test.scala we call cbt package, then read the info into memory. Call cbt clean, then cbt package, check that the file modified date is different but the jar meta data is still the same.

clean requiring confirmation is less about not trusting the user having called the right command, more about not trusting cbt as a fast moving code base to not silently break behavior here at some point. Once we get to the point where we have solid, in-memory filesystem test for this, we can remove the check.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cvogt, should the checksum be generated and checked in the master test file then?

val pass2 = super.`package`.map(f => hash(f.toPath))

assert(
pass1 == pass2,
"The checksums of jars generated during two separate builds did not match. " +
"The build is not reproducible."
)
ExitCode.Success
}

}
17 changes: 15 additions & 2 deletions stage2/Lib.scala
Original file line number Diff line number Diff line change
Expand Up @@ -257,20 +257,33 @@ final class Lib(val logger: Logger) extends
m
}

/** Create a jar from the given files and optionally make it
* executable by providing a main class.
*
* Note that all build-time related information is stripped from
* the jar, in order to make it reproducible on an independent
* system. See https://reproducible-builds.org/ for more
* information on reproducible builds. */
def createJar( jarFile: File, files: Seq[File], mainClass: Option[String] = None ): Option[File] = {
deleteIfExists(jarFile.toPath)
if( files.isEmpty ){
None
} else {
jarFile.getParentFile.mkdirs
logger.lib("Start packaging "++jarFile.string)
val jar = new JarOutputStream(new FileOutputStream(jarFile), createManifest(mainClass))
val jar = new JarOutputStream(new FileOutputStream(jarFile))
try{
val manifestEntry = new JarEntry( "META-INF/MANIFEST.MF" )
manifestEntry.setTime(0l)
jar.putNextEntry(manifestEntry)
createManifest(mainClass).write(jar)
jar.closeEntry

assert( files.forall(_.exists) )
autoRelative(files).collect{
case (file, relative) if file.isFile =>
val entry = new JarEntry( relative )
entry.setTime(file.lastModified)
entry.setTime(0l)
jar.putNextEntry(entry)
jar.write( readAllBytes( file.toPath ) )
jar.closeEntry
Expand Down
5 changes: 5 additions & 0 deletions test/test.scala
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,11 @@ object Main{
assert(res.exit0)
}

{
val res = runCbt("../examples/package-example", Seq("run"))
assert(res.exit0)
}

/*
// currently fails with
// java.lang.UnsupportedOperationException: scalafix.rewrite.ScalafixMirror.fromMirror $anon#typeSignature requires the semantic api
Expand Down