/* Copyright 2010 Aaron J. Radke Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ // vim: set ts=3 sw=3 et: package cc.drx object Classpath{ def apply(paths:Iterable[File]):Classpath = new Classpath(paths.toList.distinct) //distict prevents double inclusion def apply():Classpath = new Classpath(Nil) //[How to print out the current project classpath](https://www.mkyong.com/java/how-to-print-out-the-current-project-classpath/) def apply(cl:java.lang.ClassLoader):Classpath = { //Note: there are good reasons why this is not an existing functionality so consider it a hack //java 9 and above made it clear the type of class loader needs to be evaluated... //java 9 and up the classlaoader is not by default an instance of the URLClassLoader //see https://stackoverflow.com/a/46709196/622016 cl match { //-- java8 style case x:java.net.URLClassLoader => apply(x.getURLs) case _ => val loaderName = cl.getClass.getName //-- java9 style hack to get cp inspired from https://stackoverflow.com/q/49557431/622016 //Note: requires java opts --add-opens java.base/jdk.internal.loader=ALL-UNNAMED if(loaderName endsWith "jdk.internal.loader.ClassLoaders$AppClassLoader"){ //java10 name... Try{ //--unsafe reflection trickery to get internal java9/10 apis's for the urls val ucp:Any = JailBreak(cl)("ucp") JailBreak(ucp,"jdk.internal.loader.URLClassPath").invoke[Array[java.net.URL]]("getURLs") } match { case Success(urls) => apply(urls) case Failure(e) => Console.err.println(s"Error: $e") Console.err.println("Note using this hack for java9 or above requires java options flags: `--add-opens java.base/jdk.internal.loader=ALL-UNNAMED`") defaultClasspathWithWarning } } //--try falling back to java properties else{ Console.err.println(s"Warning: $loaderName is not a URLClassLoader to obtain obtain a classpath from URLs") defaultClasspathWithWarning //return the empty classpath... for now instead of an exception } } } private def defaultClasspathWithWarning:Classpath = { Console.err.println(s"Warning: could not get a URL classloader to collect a classPath") Console.err.println(s"Note: falling back to the Java property java.class.path") fromJavaProperties } private def apply(urls:Array[java.net.URL]):Classpath = apply(urls.map{url => File(url.getFile)}) //-- system options def system:Classpath = apply(java.lang.ClassLoader.getSystemClassLoader()) def local:Classpath = apply(getClass.getClassLoader) def fromJavaProperties:Classpath = { Option(System.getProperty("java.class.path")) match { case Some(cpProperty) => val paths = cpProperty split System.getProperty("path.separator") map File.apply apply(paths) case None => //null property case Console.err.println(s"Warning: No java.class.path property has been set.") Classpath(Nil) //fall back to an empty classpath } } } class Classpath(val files:List[File]){ // 2017-07-30("use files instead", "dk") // def cp = files def +(f:File) = Classpath(files :+ f) def ++(that:Classpath):Classpath = Classpath(files ++ that.files) def ++(that:Iterable[File]) = Classpath(files ++ that) private val sep = if(OS.kind == OS.Windows) ";" else ":" override def toString = files.map{_.abs.path} mkString sep //TODO handle spaces??? def nice:String = { if(files.isEmpty) "" else { val maxWidth = files.map{_.name.size}.max val lines = for(f <- files; dir <- f.parent) yield { def bad(c:Char) = c == '_' || c == '.' || c.isDigit val (pre,post) = f.name span {c => !bad(c)} val name = Color.Green.ansi(pre)+post name.fit(maxWidth+2) + dir.path + (if(dir.isFile || dir.isDir) "" else Red ansi " missing") } lines mkString "\n" } } //TODO make a walk // def walk:Iterable[File] = ??? }