aboutsummaryrefslogtreecommitdiff
path: root/libraries/file
diff options
context:
space:
mode:
authorChristopher Vogt <oss.nsp@cvogt.org>2017-03-20 22:09:38 -0400
committerChristopher Vogt <oss.nsp@cvogt.org>2017-03-27 19:56:13 -0400
commitbba2abe7ee38b8903822a07578c46466923d13ed (patch)
treea357fb8def6f58a9ea9a37411f3f5640dcb525fe /libraries/file
parentd2f8cade709b7d55a93e18592b6e38247d648ca9 (diff)
downloadcbt-bba2abe7ee38b8903822a07578c46466923d13ed.tar.gz
cbt-bba2abe7ee38b8903822a07578c46466923d13ed.tar.bz2
cbt-bba2abe7ee38b8903822a07578c46466923d13ed.zip
start modularizing cbt into libraries
this extracts certain parts of cbt into stand-alone libraries, which can be published to maven and used outside of cbt. This also adds scalariform for these parts of the code. This slows down cbt’s own build a lot because of the number of projects involved! So we’ll follow this by a bunch of performance tweak commits.
Diffstat (limited to 'libraries/file')
-rw-r--r--libraries/file/build/build.scala8
-rw-r--r--libraries/file/build/build/build.scala5
-rw-r--r--libraries/file/file.scala112
3 files changed, 125 insertions, 0 deletions
diff --git a/libraries/file/build/build.scala b/libraries/file/build/build.scala
new file mode 100644
index 0000000..d9017a1
--- /dev/null
+++ b/libraries/file/build/build.scala
@@ -0,0 +1,8 @@
+package cbt_build.reflect
+import cbt._
+import cbt_internal._
+class Build(val context: Context) extends Library{
+ override def inceptionYear = 2017
+ override def description = "helpers to work with java io and nio"
+ override def dependencies = super.dependencies :+ libraries.common_1
+}
diff --git a/libraries/file/build/build/build.scala b/libraries/file/build/build/build.scala
new file mode 100644
index 0000000..d3f98ce
--- /dev/null
+++ b/libraries/file/build/build/build.scala
@@ -0,0 +1,5 @@
+package cbt_build.reflect.build
+import cbt._
+class Build(val context: Context) extends BuildBuild with CbtInternal{
+ override def dependencies = super.dependencies :+ cbtInternal.library
+}
diff --git a/libraries/file/file.scala b/libraries/file/file.scala
new file mode 100644
index 0000000..f20c9a8
--- /dev/null
+++ b/libraries/file/file.scala
@@ -0,0 +1,112 @@
+package cbt.file
+import java.io.File
+import java.nio.file.Files._
+import java.nio.file.StandardCopyOption._
+import cbt.common_1._
+object `package` extends Module {
+ implicit class CbtFileOps( val file: File ) extends ops.CbtFileOps
+}
+
+package ops {
+ trait CbtFileOps extends Any {
+ def file: File
+ def ++( s: String ): File = {
+ if ( s endsWith "/" ) throw new Exception(
+ """Trying to append a String that ends in "/" to a File would loose the trailing "/". Use .stripSuffix("/") if you need to."""
+ )
+ new File( string + s ) // PERFORMANCE HOTSPOT
+ }
+ def /( s: String ): File = {
+ new File( file, s )
+ }
+ def parent = realpath( file / ".." )
+ def string = file.toString
+
+ def listOrFail: Seq[File] = Option( file.listFiles ).getOrElse( throw new Exception( "no such file: " + file ) ).toVector
+ def listRecursive: Seq[File] =
+ file +: (
+ if ( file.isDirectory ) file.listOrFail.flatMap( _.listRecursive ).toVector else Vector[File]()
+ )
+
+ def lastModifiedRecursive = listRecursive.map( _.lastModified ).max
+
+ def readAsString = new String( readAllBytes( file.toPath ) )
+
+ def quote = s"""new _root_.java.io.File(${string.quote})"""
+ }
+}
+
+trait Module {
+ def realpath( name: File ) = new File( java.nio.file.Paths.get( name.getAbsolutePath ).normalize.toString )
+ def transformFiles( files: Seq[File], transform: String => String ): Seq[File] = {
+ transformFilesOrError( files, s => Right( transform( s ) ) )._1
+ }
+
+ def transformFilesOrError[T]( files: Seq[File], transform: String => Either[T, String] ): ( Seq[File], Seq[( File, T )] ) = {
+ val results = files.map { file =>
+ val string = file.readAsString
+ transform( string ).left.map(
+ file -> _
+ ).right.map(
+ replaced =>
+ if ( string != replaced ) {
+ val tmpFile = file ++ ".cbt-tmp"
+ assert( !tmpFile.exists )
+ write( tmpFile.toPath, replaced.getBytes )
+ move( tmpFile.toPath, file.toPath, REPLACE_EXISTING )
+ Some( file )
+ } else None
+ )
+ }
+
+ ( results.map( _.right.toOption ).flatten.flatten, results.map( _.left.toOption ).flatten )
+ }
+
+ def autoRelative(
+ files: Seq[File], collector: PartialFunction[( File, String ), String] = { case ( _, r ) => r },
+ allowDuplicates: Boolean = false
+ ): Seq[( File, String )] = {
+ val map = files.sorted.flatMap { base =>
+ val b = base.getCanonicalFile.string
+ if ( base.isDirectory ) {
+ base.listRecursive.map { f =>
+ f -> f.getCanonicalFile.string.stripPrefix( b ).stripPrefix( File.separator )
+ }
+ } else {
+ Seq( base -> base.getName )
+ }
+ }.collect {
+ case v @ ( file, _ ) if collector.isDefinedAt( v ) => file -> collector( v )
+ }
+ if ( !allowDuplicates ) {
+ val relatives = map.unzip._2
+ val duplicateFiles = ( relatives diff relatives.distinct ).distinct
+ assert(
+ duplicateFiles.isEmpty, {
+ val rs = relatives.toSet
+ "Conflicting:\n\n" +
+ map.filter( rs contains _._2 ).groupBy( _._2 ).mapValues( _.map( _._1 ).sorted ).toSeq.sortBy( _._1 ).map {
+ case ( name, files ) => s"$name:\n" ++ files.mkString( "\n" )
+ }.mkString( "\n\n" )
+ }
+ )
+ }
+ map
+ }
+
+ /* recursively deletes folders*/
+ def deleteRecursive( file: File ): Unit = {
+ val s = file.string
+ // some desperate attempts to keep people from accidentally deleting their hard drive
+ assert( file === file.getCanonicalFile, "deleteRecursive requires previous .getCanonicalFile" )
+ assert( file.isAbsolute, "deleteRecursive requires absolute path" )
+ assert( file.string != "", "deleteRecursive requires non-empty file path" )
+ assert( s.split( File.separator.replace( "\\", "\\\\" ) ).size > 4, "deleteRecursive requires absolute path of at least depth 4" )
+ assert( !file.listRecursive.exists( _.isHidden ), "deleteRecursive requires no files to be hidden" )
+ assert( file.listRecursive.forall( _.canWrite ), "deleteRecursive requires all files to be writable" )
+ if ( file.isDirectory ) {
+ file.listOrFail.map( deleteRecursive )
+ }
+ file.delete
+ }
+}