mirror of https://github.com/sbt/sbt.git
Use xsbt for launch, compile, run, console, and doc.
Build Scala version can now be different from the version used by sbt and the project definition. Remove sbt compiler plugin, which is now provided by xsbt
This commit is contained in:
parent
776efa9100
commit
9bb813a2fc
25
boot/LICENSE
25
boot/LICENSE
|
|
@ -1,25 +0,0 @@
|
|||
Copyright (c) 2009 Mark Harrah
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
35
boot/NOTICE
35
boot/NOTICE
|
|
@ -1,35 +0,0 @@
|
|||
Simple Build Tool (sbt)
|
||||
Copyright 2009 Mark Harrah
|
||||
|
||||
Parts of the Scala library are distributed with sbt:
|
||||
Copyright 2002-2008 EPFL, Lausanne
|
||||
Licensed under BSD-style license (see licenses/LICENSE_Scala)
|
||||
|
||||
JLine is distributed with sbt:
|
||||
Copyright (c) 2002-2006, Marc Prud'hommeaux <mwp1@cornell.edu>
|
||||
Licensed under BSD-style license (see licenses/LICENSE_JLine)
|
||||
|
||||
Apache Ivy, licensed under the Apache License, Version 2.0
|
||||
(see licenses/LICENSE_Apache) is distributed with sbt and
|
||||
requires the following notice:
|
||||
|
||||
This product includes software developed by
|
||||
The Apache Software Foundation (http://www.apache.org/).
|
||||
|
||||
Portions of Ivy were originally developed by
|
||||
Jayasoft SARL (http://www.jayasoft.fr/)
|
||||
and are licensed to the Apache Software Foundation under the
|
||||
"Software Grant License Agreement"
|
||||
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Binary file not shown.
|
|
@ -1,258 +0,0 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
License for JCraft JSch package
|
||||
------------------------------------------------------------------------------
|
||||
Copyright (c) 2002,2003,2004,2005,2006,2007 Atsuhiko Yamanaka, JCraft,Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
License for jQuery
|
||||
------------------------------------------------------------------------------
|
||||
Copyright (c) 2007 John Resig, http://jquery.com/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
Copyright (c) 2002-2006, Marc Prud'hommeaux <mwp1@cornell.edu>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or
|
||||
without modification, are permitted provided that the following
|
||||
conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with
|
||||
the distribution.
|
||||
|
||||
Neither the name of JLine nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
|
||||
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
SCALA LICENSE
|
||||
|
||||
Copyright (c) 2002-2008 EPFL, Lausanne, unless otherwise specified.
|
||||
All rights reserved.
|
||||
|
||||
This software was developed by the Programming Methods Laboratory of the
|
||||
Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software in source
|
||||
or binary form for any purpose with or without fee is hereby granted,
|
||||
provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the EPFL nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
|
@ -1,350 +0,0 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2009 Mark Harrah
|
||||
*/
|
||||
package sbt.boot
|
||||
|
||||
// This is the main class for the sbt launcher. Its purpose is to ensure the appropriate
|
||||
// versions of sbt and scala are downloaded to the projects 'project/boot' directory.
|
||||
// Then, the downloaded version of sbt is started as usual using the right version of
|
||||
// scala.
|
||||
|
||||
// Artifact names must be consistent between the main sbt build and this build.
|
||||
|
||||
import java.io.{File, FileFilter}
|
||||
import java.net.{MalformedURLException, URL, URLClassLoader}
|
||||
|
||||
// contains constants and paths
|
||||
import BootConfiguration._
|
||||
import UpdateTarget.{UpdateScala, UpdateSbt}
|
||||
|
||||
// The exception to use when an error occurs at the launcher level (and not a nested exception).
|
||||
// This indicates overrides toString because the exception class name is not needed to understand
|
||||
// the error message.
|
||||
private class BootException(override val toString: String) extends RuntimeException
|
||||
// The entry point to the launcher
|
||||
object Boot
|
||||
{
|
||||
def main(args: Array[String])
|
||||
{
|
||||
System.setProperty("sbt.boot", true.toString)
|
||||
val found = Paths.projectSearch().getCanonicalPath
|
||||
System.setProperty("user.dir", found)
|
||||
checkProxy()
|
||||
try { boot(args) }
|
||||
catch
|
||||
{
|
||||
case b: BootException => errorAndExit(b)
|
||||
case e =>
|
||||
e.printStackTrace
|
||||
errorAndExit(e)
|
||||
}
|
||||
System.exit(0)
|
||||
}
|
||||
private def errorAndExit(e: Throwable)
|
||||
{
|
||||
System.out.println("Error during sbt execution: " + e.toString)
|
||||
System.exit(1)
|
||||
}
|
||||
def boot(args: Array[String])
|
||||
{
|
||||
// prompt to create project if it doesn't exist.
|
||||
// will not return if user declines
|
||||
(new Paths).checkProject()
|
||||
val loaderCache = new LoaderCache
|
||||
if(args.length == 0)
|
||||
load(args, loaderCache) // interactive mode, which can only use one version of scala for a run
|
||||
else
|
||||
runBatch(args.toList, Nil, loaderCache) // batch mode, which can reboot with a different scala version
|
||||
}
|
||||
private def runBatch(args: List[String], accumulateReversed: List[String], loaderCache: LoaderCache)
|
||||
{
|
||||
def doLoad() = if(!accumulateReversed.isEmpty) load(accumulateReversed.reverse.toArray, loaderCache)
|
||||
args match
|
||||
{
|
||||
case Nil => doLoad()
|
||||
case RebootCommand :: tail =>
|
||||
doLoad()
|
||||
runBatch(tail, Nil, loaderCache)
|
||||
case action :: tail if action.trim.startsWith(CrossBuildPrefix) =>
|
||||
doLoad()
|
||||
load(Array(action), loaderCache) // call main with the single cross-build argument, preserving the '+' prefix, with which it knows what to do
|
||||
runBatch(tail, Nil, loaderCache)
|
||||
case notReload :: tail => runBatch(tail, notReload :: accumulateReversed, loaderCache)
|
||||
}
|
||||
}
|
||||
/** Loads the project in the current working directory using the version of scala and sbt
|
||||
* declared in the build. The class loader used prevents the Scala and Ivy classes used by
|
||||
* this loader from being seen by the loaded sbt/project.*/
|
||||
private def load(args: Array[String], loaderCache: LoaderCache)
|
||||
{
|
||||
val loader = (new Setup(loaderCache)).loader()
|
||||
val sbtMain = Class.forName(SbtMainClass, true, loader)
|
||||
val exitCode = run(sbtMain, args)
|
||||
if(exitCode == NormalExitCode)
|
||||
()
|
||||
else if(exitCode == RebootExitCode)
|
||||
load(args, loaderCache)
|
||||
else
|
||||
System.exit(exitCode)
|
||||
}
|
||||
private def run(sbtMain: Class[_], args: Array[String]): Int =
|
||||
{
|
||||
try {
|
||||
// Versions newer than 0.3.8 enter through the run method, which does not call System.exit
|
||||
val runMethod = sbtMain.getMethod(MainMethodName, classOf[Array[String]])
|
||||
runMethod.invoke(null, Array(args) : _*).asInstanceOf[Int]
|
||||
} catch {
|
||||
case e: NoSuchMethodException => runOld(sbtMain, args)
|
||||
}
|
||||
}
|
||||
/** The entry point for version 0.3.8 was the main method. */
|
||||
private def runOld(sbtMain: Class[_], args: Array[String]): Int =
|
||||
{
|
||||
val runMethod = sbtMain.getMethod(OldMainMethodName, classOf[Array[String]])
|
||||
runMethod.invoke(null, Array(args) : _*)
|
||||
NormalExitCode
|
||||
}
|
||||
|
||||
private def checkProxy()
|
||||
{
|
||||
import ProxyProperties._
|
||||
val httpProxy = System.getenv(HttpProxyEnv)
|
||||
if(isDefined(httpProxy) && !isPropertyDefined(ProxyHost) && !isPropertyDefined(ProxyPort))
|
||||
{
|
||||
try
|
||||
{
|
||||
val proxy = new URL(httpProxy)
|
||||
setProperty(ProxyHost, proxy.getHost)
|
||||
val port = proxy.getPort
|
||||
if(port >= 0)
|
||||
System.setProperty(ProxyPort, port.toString)
|
||||
copyEnv(HttpProxyUser, ProxyUser)
|
||||
copyEnv(HttpProxyPassword, ProxyPassword)
|
||||
}
|
||||
catch
|
||||
{
|
||||
case e: MalformedURLException =>
|
||||
System.out.println("Warning: could not parse http_proxy setting: " + e.toString)
|
||||
}
|
||||
}
|
||||
}
|
||||
private def copyEnv(envKey: String, sysKey: String) { setProperty(sysKey, System.getenv(envKey)) }
|
||||
private def setProperty(key: String, value: String) { if(value != null) System.setProperty(key, value) }
|
||||
private def isPropertyDefined(k: String) = isDefined(System.getProperty(k))
|
||||
private def isDefined(s: String) = s != null && !s.isEmpty
|
||||
}
|
||||
|
||||
object Paths
|
||||
{
|
||||
val Only = "only"
|
||||
val RootFirst = "root-first"
|
||||
val Nearest = "nearest"
|
||||
val NoSearch = "none"
|
||||
val SearchPropertyName = "sbt.boot.search"
|
||||
def projectSearch() =
|
||||
{
|
||||
val current = (new File(".")) getCanonicalFile
|
||||
lazy val fromRoot = path(current, Nil).filter(hasProject).map(_.getCanonicalFile)
|
||||
val found: Option[File] =
|
||||
(System.getProperty(SearchPropertyName, NoSearch).trim.toLowerCase match
|
||||
{
|
||||
case RootFirst => fromRoot.headOption
|
||||
case Nearest => fromRoot.reverse.headOption
|
||||
case Only =>
|
||||
if(hasProject(current))
|
||||
Some(current)
|
||||
else
|
||||
fromRoot match
|
||||
{
|
||||
case Nil => Some(current)
|
||||
case head :: Nil => Some(head)
|
||||
case xs =>
|
||||
System.err.println("Search method is 'only' and multiple ancestor directories contain project/build.properties:\n\t" + fromRoot.mkString("\n\t"))
|
||||
System.exit(1)
|
||||
None
|
||||
}
|
||||
case _ => Some(current)
|
||||
})
|
||||
found.getOrElse(current)
|
||||
}
|
||||
private def hasProject(f: File) = new Paths(f) hasPropertiesFile
|
||||
private def path(f: File, acc: List[File]): List[File] = if(f eq null) acc else path(f.getParentFile, f :: acc)
|
||||
}
|
||||
private class Paths(baseDirectory: File) extends NotNull
|
||||
{
|
||||
def this() = this(new File(".") getCanonicalFile)
|
||||
protected final val ProjectDirectory = new File(baseDirectory, ProjectDirectoryName)
|
||||
protected final val BootDirectory = new File(ProjectDirectory, BootDirectoryName)
|
||||
protected final val PropertiesFile = new File(ProjectDirectory, BuildPropertiesName)
|
||||
|
||||
final def hasPropertiesFile = PropertiesFile.exists
|
||||
final def checkProject()
|
||||
{
|
||||
if(!ProjectDirectory.exists)
|
||||
{
|
||||
val line = SimpleReader.readLine("Project does not exist, create new project? (y/N/s) : ")
|
||||
if(Setup.isYes(line))
|
||||
ProjectProperties(PropertiesFile, true)
|
||||
else if(Setup.isScratch(line))
|
||||
ProjectProperties.scratch(PropertiesFile)
|
||||
else
|
||||
System.exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
/** A class to handle setting up the properties and classpath of the project
|
||||
* before it is loaded. */
|
||||
private class Setup(loaderCache: LoaderCache) extends Paths
|
||||
{
|
||||
/** Checks that the requested version of sbt and scala have been downloaded.
|
||||
* It performs a simple check that the appropriate directories exist. It uses Ivy
|
||||
* to resolve and retrieve any necessary libraries. The classpath to use is returned.*/
|
||||
final def loader(): ClassLoader = loader(Nil)
|
||||
private final def loader(forcePrompt: Seq[String]): ClassLoader =
|
||||
{
|
||||
val (normalScalaVersion, sbtVersion) = ProjectProperties.forcePrompt(PropertiesFile, forcePrompt : _*)
|
||||
val scalaVersion = crossScalaVersion(normalScalaVersion)
|
||||
loaderCache( scalaVersion, sbtVersion ) match
|
||||
{
|
||||
case Some(existingLoader) =>
|
||||
{
|
||||
setScalaVersion(scalaVersion)
|
||||
existingLoader
|
||||
}
|
||||
case None =>
|
||||
{
|
||||
getLoader(scalaVersion, sbtVersion) match
|
||||
{
|
||||
case Left(retry) => loader(retry)
|
||||
case Right(classLoader) => classLoader
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private def crossScalaVersion(simpleScalaVersion: String): String =
|
||||
{
|
||||
val crossScalaVersion = System.getProperty(SbtScalaVersionKey)
|
||||
if(crossScalaVersion == null || crossScalaVersion.isEmpty)
|
||||
simpleScalaVersion
|
||||
else
|
||||
crossScalaVersion
|
||||
}
|
||||
private def getLoader(scalaVersion: String, sbtVersion: String): Either[Seq[String], ClassLoader] =
|
||||
{
|
||||
import Setup.{failIfMissing,isYes,needsUpdate}
|
||||
import ProjectProperties.{ScalaVersionKey, SbtVersionKey}
|
||||
|
||||
val baseDirectory = new File(BootDirectory, baseDirectoryName(scalaVersion))
|
||||
System.setProperty(ScalaHomeProperty, baseDirectory.getAbsolutePath)
|
||||
val scalaDirectory = new File(baseDirectory, ScalaDirectoryName)
|
||||
val sbtDirectory = new File(baseDirectory, sbtDirectoryName(sbtVersion))
|
||||
|
||||
val classLoader = createLoader(scalaDirectory, sbtDirectory)
|
||||
val updateTargets = needsUpdate("", classLoader, TestLoadScalaClasses, UpdateScala) ::: needsUpdate(sbtVersion, classLoader, TestLoadSbtClasses, UpdateSbt)
|
||||
if(updateTargets.isEmpty) // avoid loading Ivy related classes if there is nothing to update
|
||||
success(classLoader, scalaVersion, sbtVersion)
|
||||
else
|
||||
{
|
||||
Update(baseDirectory, sbtVersion, scalaVersion, updateTargets: _*)
|
||||
|
||||
val classLoader = createLoader(scalaDirectory, sbtDirectory)
|
||||
val sbtFailed = failIfMissing(classLoader, TestLoadSbtClasses, "sbt " + sbtVersion, SbtVersionKey)
|
||||
val scalaFailed = failIfMissing(classLoader, TestLoadScalaClasses, "Scala " + scalaVersion, ScalaVersionKey)
|
||||
|
||||
(scalaFailed +++ sbtFailed) match
|
||||
{
|
||||
case Success => success(classLoader, scalaVersion, sbtVersion)
|
||||
case f: Failure =>
|
||||
val noRetrieveMessage = "Could not retrieve " + f.label + "."
|
||||
val getNewVersions = SimpleReader.readLine(noRetrieveMessage + " Select different version? (y/N) : ")
|
||||
if(isYes(getNewVersions))
|
||||
Left(f.keys)
|
||||
else
|
||||
throw new BootException(noRetrieveMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
private def success(classLoader: ClassLoader, scalaVersion: String, sbtVersion: String) =
|
||||
{
|
||||
setScalaVersion(scalaVersion)
|
||||
loaderCache( scalaVersion, sbtVersion ) = classLoader
|
||||
Right(classLoader)
|
||||
}
|
||||
private def createLoader(dirs: File*) =
|
||||
{
|
||||
val classpath = Setup.getJars(dirs : _*)
|
||||
new URLClassLoader(classpath.toArray, new BootFilteredLoader)
|
||||
}
|
||||
private def setScalaVersion(scalaVersion: String) { System.setProperty(SbtScalaVersionKey, scalaVersion) }
|
||||
}
|
||||
private final class LoaderCache
|
||||
{
|
||||
private[this] var cachedSbtVersion: Option[String] = None
|
||||
private[this] val loaderMap = new scala.collection.mutable.HashMap[String, ClassLoader]
|
||||
def apply(scalaVersion: String, sbtVersion: String): Option[ClassLoader] =
|
||||
{
|
||||
cachedSbtVersion flatMap { currentSbtVersion =>
|
||||
if(sbtVersion == currentSbtVersion)
|
||||
loaderMap.get(scalaVersion)
|
||||
else
|
||||
None
|
||||
}
|
||||
}
|
||||
def update(scalaVersion: String, sbtVersion: String, loader: ClassLoader)
|
||||
{
|
||||
for(currentSbtVersion <- cachedSbtVersion)
|
||||
{
|
||||
if(sbtVersion != currentSbtVersion)
|
||||
loaderMap.clear()
|
||||
}
|
||||
cachedSbtVersion = Some(sbtVersion)
|
||||
loaderMap(scalaVersion) = loader
|
||||
}
|
||||
}
|
||||
private object Setup
|
||||
{
|
||||
private def failIfMissing(loader: ClassLoader, classes: Iterable[String], label: String, key: String) = checkTarget(loader, classes, Success, new Failure(label, List(key)))
|
||||
private def needsUpdate(version: String, loader: ClassLoader, classes: Iterable[String], target: UpdateTarget.Value) =
|
||||
if(version.endsWith("-SNAPSHOT"))
|
||||
target :: Nil
|
||||
else
|
||||
checkTarget(loader, classes, Nil, target :: Nil)
|
||||
private def checkTarget[T](loader: ClassLoader, classes: Iterable[String], ifSuccess: => T, ifFailure: => T): T =
|
||||
{
|
||||
try
|
||||
{
|
||||
for(c <- classes)
|
||||
Class.forName(c, false, loader)
|
||||
ifSuccess
|
||||
}
|
||||
catch { case e: ClassNotFoundException => ifFailure }
|
||||
}
|
||||
def isYes(so: Option[String]) = isValue("y", "yes")(so)
|
||||
def isScratch(so: Option[String]) = isValue("s", "scratch")(so)
|
||||
def isValue(values: String*)(so: Option[String]) =
|
||||
so match
|
||||
{
|
||||
case Some(s) => values.contains(s.toLowerCase)
|
||||
case None => false
|
||||
}
|
||||
private def getJars(directories: File*) = directories.flatMap(file => wrapNull(file.listFiles(JarFilter))).map(_.toURI.toURL)
|
||||
private def wrapNull(a: Array[File]): Array[File] = if(a == null) Array() else a
|
||||
}
|
||||
|
||||
|
||||
private object JarFilter extends FileFilter
|
||||
{
|
||||
def accept(file: File) = !file.isDirectory && file.getName.endsWith(".jar")
|
||||
}
|
||||
|
||||
private sealed trait Checked extends NotNull { def +++(o: Checked): Checked }
|
||||
private final object Success extends Checked { def +++(o: Checked) = o }
|
||||
private final class Failure(val label: String, val keys: List[String]) extends Checked
|
||||
{
|
||||
def +++(o: Checked) =
|
||||
o match
|
||||
{
|
||||
case Success => this
|
||||
case f: Failure => new Failure(label + " and " + f.label, keys ::: f.keys)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2009 Mark Harrah
|
||||
*/
|
||||
package sbt.boot
|
||||
|
||||
// project/boot/ [BootDirectoryName]
|
||||
// scala-<version>/ [baseDirectoryName]
|
||||
// lib/ [ScalaDirectoryName]
|
||||
// sbt-<version>/ [sbtDirectoryName]
|
||||
//
|
||||
// see also ProjectProperties for the set of constants that apply to the build.properties file in a project
|
||||
private object BootConfiguration
|
||||
{
|
||||
val SbtMainClass = "sbt.Main"
|
||||
val MainMethodName = "run"
|
||||
val OldMainMethodName = "main"
|
||||
|
||||
// these are the module identifiers to resolve/retrieve
|
||||
val ScalaOrg = "org.scala-lang"
|
||||
val SbtOrg = "sbt"
|
||||
val CompilerModuleName = "scala-compiler"
|
||||
val LibraryModuleName = "scala-library"
|
||||
val SbtModuleName = "simple-build-tool"
|
||||
|
||||
/** The Ivy conflict manager to use for updating.*/
|
||||
val ConflictManagerName = "strict"
|
||||
/** The name of the local Ivy repository, which is used when compiling sbt from source.*/
|
||||
val LocalIvyName = "local"
|
||||
/** The pattern used for the local Ivy repository, which is used when compiling sbt from source.*/
|
||||
val LocalPattern = "[organisation]/[module]/[revision]/[type]s/[artifact].[ext]"
|
||||
/** The artifact pattern used for the local Ivy repository.*/
|
||||
def LocalArtifactPattern = LocalPattern
|
||||
/** The Ivy pattern used for the local Ivy repository.*/
|
||||
def LocalIvyPattern = LocalPattern
|
||||
|
||||
/** The name of the property declaring the version of scala to use to build the project when not cross-building.*/
|
||||
val ScalaVersion = "scala.version"
|
||||
/** The name of the property declaring the version of sbt to use to build the project.*/
|
||||
val SbtVersion = "sbt.version"
|
||||
/** The name of the system property containing the version of scala actually used to build a project.
|
||||
* This might be different from the ScalaVersion property when cross-building.*/
|
||||
val SbtScalaVersionKey = "sbt.scala.version"
|
||||
/** The class name prefix used to hide the Scala classes used by this loader from sbt
|
||||
* and the project definition*/
|
||||
val ScalaPackage = "scala."
|
||||
/** The class name prefix used to hide the Ivy classes used by this loader from sbt
|
||||
* and the project definition*/
|
||||
val IvyPackage = "org.apache.ivy."
|
||||
/** The loader will check that these classes can be loaded and will assume that their presence indicates
|
||||
* sbt and its dependencies have been downloaded.*/
|
||||
val TestLoadSbtClasses = "sbt.Main" :: "org.apache.ivy.Ivy" :: Nil
|
||||
/** The loader will check that these classes can be loaded and will assume that their presence indicates
|
||||
* the Scala compiler and library have been downloaded.*/
|
||||
val TestLoadScalaClasses = "scala.ScalaObject" :: "scala.tools.nsc.GenericRunnerCommand" :: Nil
|
||||
|
||||
val ProjectDirectoryName = "project"
|
||||
val BootDirectoryName = "boot"
|
||||
val BuildPropertiesName ="build.properties"
|
||||
val ScalaHomeProperty = "scala.home"
|
||||
val UpdateLogName = "update.log"
|
||||
|
||||
val CrossBuildPrefix = "+"
|
||||
val RebootCommand = "reboot"
|
||||
val RebootExitCode = -1
|
||||
val NormalExitCode = 0
|
||||
val DefaultIvyConfiguration = "default"
|
||||
|
||||
/** The base URL to use to resolve sbt for download. */
|
||||
val sbtRootBase = "http://simple-build-tool.googlecode.com/svn/artifacts/"
|
||||
/** The name of the directory within the boot directory to retrieve scala to. */
|
||||
val ScalaDirectoryName = "lib"
|
||||
/** The Ivy pattern to use for retrieving the scala compiler and library. It is relative to the directory
|
||||
* containing all jars for the requested version of scala. */
|
||||
val scalaRetrievePattern = ScalaDirectoryName + "/[artifact].[ext]"
|
||||
|
||||
/** The Ivy pattern to use for retrieving sbt and its dependencies. It is relative to the directory
|
||||
* containing all jars for the requested version of scala. */
|
||||
def sbtRetrievePattern(sbtVersion: String) = sbtDirectoryName(sbtVersion) + "/[artifact]-[revision].[ext]"
|
||||
/** The Ivy pattern to use for resolving sbt and its dependencies from the Google code project.*/
|
||||
def sbtResolverPattern(scalaVersion: String) = sbtRootBase + "[revision]/[type]s/[artifact].[ext]"
|
||||
/** The name of the directory to retrieve sbt and its dependencies to.*/
|
||||
def sbtDirectoryName(sbtVersion: String) = SbtOrg + "-" + sbtVersion
|
||||
/** The name of the directory in the boot directory to put all jars for the given version of scala in.*/
|
||||
def baseDirectoryName(scalaVersion: String) = "scala-" + scalaVersion
|
||||
}
|
||||
private object ProxyProperties
|
||||
{
|
||||
val HttpProxyEnv = "http_proxy"
|
||||
val HttpProxyUser = "http_proxy_user"
|
||||
val HttpProxyPassword = "http_proxy_pass"
|
||||
|
||||
val ProxyHost = "http.proxyHost"
|
||||
val ProxyPort = "http.proxyPort"
|
||||
val ProxyUser = "http.proxyUser"
|
||||
val ProxyPassword = "http.proxyPassword"
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2009 Mark Harrah
|
||||
*/
|
||||
package sbt.boot
|
||||
|
||||
import BootConfiguration._
|
||||
|
||||
/** A custom class loader to ensure the main part of sbt doesn't load any Scala or
|
||||
* Ivy classes from the jar containing the loader. */
|
||||
private[boot] final class BootFilteredLoader extends ClassLoader with NotNull
|
||||
{
|
||||
@throws(classOf[ClassNotFoundException])
|
||||
override final def loadClass(className: String, resolve: Boolean): Class[_] =
|
||||
{
|
||||
if(className.startsWith(ScalaPackage) || className.startsWith(IvyPackage))
|
||||
throw new ClassNotFoundException(className)
|
||||
else
|
||||
super.loadClass(className, resolve)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,147 +0,0 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2009 Mark Harrah
|
||||
*/
|
||||
package sbt.boot
|
||||
|
||||
/*
|
||||
Project does not exist, create new project? [y/N/s] y
|
||||
Name:
|
||||
Organization []:
|
||||
Version [1.0]:
|
||||
Scala version [2.7.5]:
|
||||
sbt version [0.5.4]:
|
||||
*/
|
||||
import java.io.File
|
||||
/** Constants related to reading/writing the build.properties file in a project.
|
||||
* See BootConfiguration for general constants used by the loader. */
|
||||
private object ProjectProperties
|
||||
{
|
||||
/** The properties key for storing the name of the project.*/
|
||||
val NameKey = "project.name"
|
||||
/** The properties key for storing the organization of the project.*/
|
||||
val OrganizationKey = "project.organization"
|
||||
/** The properties key for storing the version of the project.*/
|
||||
val VersionKey = "project.version"
|
||||
/** The properties key for storing the version of Scala used with the project.*/
|
||||
val ScalaVersionKey = "scala.version"
|
||||
/** The properties key for storing the version of sbt used to build the project.*/
|
||||
val SbtVersionKey = "sbt.version"
|
||||
/** The properties key to communicate to the main component of sbt that the project
|
||||
* should be initialized after being loaded, typically by creating a default directory structure.*/
|
||||
val InitializeProjectKey = "project.initialize"
|
||||
/** The properties key that configures the project to be flattened a bit for use by quick throwaway projects.*/
|
||||
val ScratchKey = "project.scratch"
|
||||
|
||||
/** The label used when prompting for the name of the user's project.*/
|
||||
val NameLabel = "Name"
|
||||
/** The label used when prompting for the organization of the user's project.*/
|
||||
val OrganizationLabel = "Organization"
|
||||
/** The label used when prompting for the version of the user's project.*/
|
||||
val VersionLabel = "Version"
|
||||
/** The label used when prompting for the version of Scala to use for the user's project.*/
|
||||
val ScalaVersionLabel = "Scala version"
|
||||
/** The label used when prompting for the version of sbt to use for the user's project.*/
|
||||
val SbtVersionLabel = "sbt version"
|
||||
|
||||
/** The default organization of the new user project when the user doesn't explicitly specify one when prompted.*/
|
||||
val DefaultOrganization = ""
|
||||
/** The default version of the new user project when the user doesn't explicitly specify a version when prompted.*/
|
||||
val DefaultVersion = "1.0"
|
||||
/** The default version of sbt when the user doesn't explicitly specify a version when prompted.*/
|
||||
val DefaultSbtVersion = DefaultVersions.Sbt //"0.5.4"
|
||||
/** The default version of Scala when the user doesn't explicitly specify a version when prompted.*/
|
||||
val DefaultScalaVersion = DefaultVersions.Scala//"2.7.5"
|
||||
|
||||
// sets up the project properties for a throwaway project (flattens src and lib to the root project directory)
|
||||
def scratch(file: File)
|
||||
{
|
||||
withProperties(file) { properties =>
|
||||
for( (key, _, default, _) <- propertyDefinitions(false))
|
||||
properties(key) = default.getOrElse("scratch")
|
||||
properties(ScratchKey) = true.toString
|
||||
}
|
||||
}
|
||||
// returns (scala version, sbt version)
|
||||
def apply(file: File, setInitializeProject: Boolean): (String, String) = applyImpl(file, setInitializeProject, Nil)
|
||||
def forcePrompt(file: File, propertyKeys: String*) = applyImpl(file, false, propertyKeys)
|
||||
private def applyImpl(file: File, setInitializeProject: Boolean, propertyKeys: Iterable[String]): (String, String) =
|
||||
{
|
||||
val organizationOptional = file.exists
|
||||
withProperties(file) { properties =>
|
||||
properties -= propertyKeys
|
||||
|
||||
prompt(properties, organizationOptional)
|
||||
if(setInitializeProject)
|
||||
properties(InitializeProjectKey) = true.toString
|
||||
}
|
||||
}
|
||||
// (key, label, defaultValue, promptRequired)
|
||||
private def propertyDefinitions(organizationOptional: Boolean) =
|
||||
(NameKey, NameLabel, None, true) ::
|
||||
(OrganizationKey, OrganizationLabel, Some(DefaultOrganization), !organizationOptional) ::
|
||||
(VersionKey, VersionLabel, Some(DefaultVersion), true) ::
|
||||
(ScalaVersionKey, ScalaVersionLabel, Some(DefaultScalaVersion), true) ::
|
||||
(SbtVersionKey, SbtVersionLabel, Some(DefaultSbtVersion), true) ::
|
||||
Nil
|
||||
private def prompt(fill: ProjectProperties, organizationOptional: Boolean)
|
||||
{
|
||||
for( (key, label, default, promptRequired) <- propertyDefinitions(organizationOptional))
|
||||
{
|
||||
val value = fill(key)
|
||||
if(value == null && promptRequired)
|
||||
fill(key) = readLine(label, default)
|
||||
}
|
||||
}
|
||||
private def withProperties(file: File)(f: ProjectProperties => Unit) =
|
||||
{
|
||||
val properties = new ProjectProperties(file)
|
||||
f(properties)
|
||||
properties.save
|
||||
(properties(ScalaVersionKey), properties(SbtVersionKey))
|
||||
}
|
||||
private def readLine(label: String, default: Option[String]): String =
|
||||
{
|
||||
val prompt =
|
||||
default match
|
||||
{
|
||||
case Some(d) => "%s [%s]: ".format(label, d)
|
||||
case None => "%s: ".format(label)
|
||||
}
|
||||
SimpleReader.readLine(prompt) orElse default match
|
||||
{
|
||||
case Some(line) => line
|
||||
case None => throw new BootException("Project not loaded: " + label + " not specified.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import java.io.{FileInputStream, FileOutputStream}
|
||||
import java.util.Properties
|
||||
private class ProjectProperties(file: File) extends NotNull
|
||||
{
|
||||
private[this] var modified = false
|
||||
private[this] val properties = new Properties
|
||||
if(file.exists)
|
||||
{
|
||||
val in = new FileInputStream(file)
|
||||
try { properties.load(in) } finally { in.close() }
|
||||
}
|
||||
|
||||
def update(key: String, value: String)
|
||||
{
|
||||
modified = true
|
||||
properties.setProperty(key, value)
|
||||
}
|
||||
def apply(key: String) = properties.getProperty(key)
|
||||
def save()
|
||||
{
|
||||
if(modified)
|
||||
{
|
||||
file.getParentFile.mkdirs()
|
||||
val out = new FileOutputStream(file)
|
||||
try { properties.store(out, "Project Properties") } finally { out.close() }
|
||||
modified = false
|
||||
}
|
||||
}
|
||||
def -= (keys: Iterable[String]) { for(key <- keys) properties.remove(key) }
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2009 Mark Harrah
|
||||
*/
|
||||
package sbt.boot
|
||||
|
||||
import jline.ConsoleReader
|
||||
object SimpleReader extends NotNull
|
||||
{
|
||||
protected[this] val reader =
|
||||
{
|
||||
val cr = new ConsoleReader
|
||||
cr.setBellEnabled(false)
|
||||
cr
|
||||
}
|
||||
def readLine(prompt: String) =
|
||||
reader.readLine(prompt) match
|
||||
{
|
||||
case null => None
|
||||
case x =>
|
||||
val trimmed = x.trim
|
||||
if(trimmed.isEmpty)
|
||||
None
|
||||
else
|
||||
Some(trimmed)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,273 +0,0 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2009 Mark Harrah
|
||||
*/
|
||||
package sbt.boot
|
||||
|
||||
import java.io.{File, FileWriter, PrintWriter, Writer}
|
||||
|
||||
import org.apache.ivy.{core, plugins, util, Ivy}
|
||||
import core.LogOptions
|
||||
import core.cache.DefaultRepositoryCacheManager
|
||||
import core.event.EventManager
|
||||
import core.module.id.ModuleRevisionId
|
||||
import core.module.descriptor.{Configuration, DefaultDependencyDescriptor, DefaultModuleDescriptor, ModuleDescriptor}
|
||||
import core.report.ResolveReport
|
||||
import core.resolve.{ResolveEngine, ResolveOptions}
|
||||
import core.retrieve.{RetrieveEngine, RetrieveOptions}
|
||||
import core.sort.SortEngine
|
||||
import core.settings.IvySettings
|
||||
import plugins.resolver.{ChainResolver, FileSystemResolver, IBiblioResolver, URLResolver}
|
||||
import util.{DefaultMessageLogger, Message}
|
||||
|
||||
import BootConfiguration._
|
||||
|
||||
private[boot] object UpdateTarget extends Enumeration
|
||||
{
|
||||
val UpdateScala, UpdateSbt = Value
|
||||
}
|
||||
import UpdateTarget.{UpdateSbt, UpdateScala}
|
||||
|
||||
object Update
|
||||
{
|
||||
/** Use Ivy to resolve and retrieve the specified 'targets' for the given versions.*/
|
||||
def apply(bootDirectory: File, sbtVersion: String, scalaVersion: String, targets: UpdateTarget.Value*) =
|
||||
synchronized // synchronized because Ivy is not thread-safe
|
||||
{
|
||||
val up = new Update(bootDirectory, sbtVersion, scalaVersion, targets : _*)
|
||||
up.update()
|
||||
}
|
||||
}
|
||||
/** Ensures that the Scala and sbt jars exist for the given versions or else downloads them.*/
|
||||
private final class Update(bootDirectory: File, sbtVersion: String, scalaVersion: String, targets: UpdateTarget.Value*)
|
||||
{
|
||||
private def logFile = new File(bootDirectory, UpdateLogName)
|
||||
/** A Writer to use to write the full logging information to a file for debugging. **/
|
||||
lazy val logWriter =
|
||||
{
|
||||
bootDirectory.mkdirs
|
||||
new PrintWriter(new FileWriter(logFile))
|
||||
}
|
||||
|
||||
/** The main entry point of this class for use by the Update module. It runs Ivy */
|
||||
private def update()
|
||||
{
|
||||
Message.setDefaultLogger(new SbtIvyLogger(logWriter))
|
||||
try { targets.foreach(update) } // runs update on each module separately
|
||||
catch
|
||||
{
|
||||
case e: Exception =>
|
||||
e.printStackTrace(logWriter)
|
||||
log(e.toString)
|
||||
println(" (see " + logFile + " for complete log)")
|
||||
}
|
||||
finally { logWriter.close() }
|
||||
}
|
||||
/** Runs update for the specified target (updates either the scala or sbt jars for building the project) */
|
||||
private def update(target: UpdateTarget.Value)
|
||||
{
|
||||
val settings = new IvySettings
|
||||
val ivy = Ivy.newInstance(settings)
|
||||
ivy.pushContext()
|
||||
try { update(target, settings) }
|
||||
finally { ivy.popContext() }
|
||||
}
|
||||
private def update(target: UpdateTarget.Value, settings: IvySettings)
|
||||
{
|
||||
import Configuration.Visibility.PUBLIC
|
||||
// the actual module id here is not that important
|
||||
val moduleID = new DefaultModuleDescriptor(createID(SbtOrg, "boot", "1.0"), "release", null, false)
|
||||
moduleID.setLastModified(System.currentTimeMillis)
|
||||
moduleID.addConfiguration(new Configuration(DefaultIvyConfiguration, PUBLIC, "", Array(), true, null))
|
||||
// add dependencies based on which target needs updating
|
||||
target match
|
||||
{
|
||||
case UpdateScala =>
|
||||
addDependency(moduleID, ScalaOrg, CompilerModuleName, scalaVersion, "default")
|
||||
addDependency(moduleID, ScalaOrg, LibraryModuleName, scalaVersion, "default")
|
||||
update(settings, moduleID, target, false)
|
||||
case UpdateSbt =>
|
||||
addDependency(moduleID, SbtOrg, SbtModuleName, sbtVersion, scalaVersion)
|
||||
try { update(settings, moduleID, target, false) }
|
||||
catch
|
||||
{
|
||||
// unfortunately, there is not a more specific exception thrown when a configuration does not exist,
|
||||
// so we always retry after cleaning the ivy file for this version of sbt on in case it is a newer version
|
||||
// of Scala than when this version of sbt was initially published
|
||||
case e: RuntimeException =>
|
||||
update(settings, moduleID, target, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Runs the resolve and retrieve for the given moduleID, which has had its dependencies added already. */
|
||||
private def update(settings: IvySettings, moduleID: DefaultModuleDescriptor, target: UpdateTarget.Value, cleanExisting: Boolean)
|
||||
{
|
||||
val eventManager = new EventManager
|
||||
addResolvers(settings, scalaVersion, target)
|
||||
settings.setDefaultConflictManager(settings.getConflictManager(ConflictManagerName))
|
||||
settings.setBaseDir(bootDirectory)
|
||||
if(cleanExisting)
|
||||
{
|
||||
val sbtID = createID(SbtOrg, SbtModuleName, sbtVersion)
|
||||
onDefaultRepositoryCacheManager(settings) { cache =>
|
||||
val ivyFile = cache.getIvyFileInCache(sbtID)
|
||||
ivyFile.delete()
|
||||
val original = new File(ivyFile.getParentFile, ivyFile.getName + ".original")
|
||||
original.delete()
|
||||
}
|
||||
}
|
||||
resolve(settings, eventManager, moduleID)
|
||||
retrieve(settings, eventManager, moduleID, target)
|
||||
}
|
||||
private def createID(organization: String, name: String, revision: String) =
|
||||
ModuleRevisionId.newInstance(organization, name, revision)
|
||||
/** Adds the given dependency to the default configuration of 'moduleID'. */
|
||||
private def addDependency(moduleID: DefaultModuleDescriptor, organization: String, name: String, revision: String, conf: String)
|
||||
{
|
||||
val dep = new DefaultDependencyDescriptor(moduleID, createID(organization, name, revision), false, false, true)
|
||||
dep.addDependencyConfiguration(DefaultIvyConfiguration, conf)
|
||||
moduleID.addDependency(dep)
|
||||
}
|
||||
private def resolve(settings: IvySettings, eventManager: EventManager, module: ModuleDescriptor)
|
||||
{
|
||||
val resolveOptions = new ResolveOptions
|
||||
// this reduces the substantial logging done by Ivy, including the progress dots when downloading artifacts
|
||||
resolveOptions.setLog(LogOptions.LOG_DOWNLOAD_ONLY)
|
||||
val resolveEngine = new ResolveEngine(settings, eventManager, new SortEngine(settings))
|
||||
val resolveReport = resolveEngine.resolve(module, resolveOptions)
|
||||
if(resolveReport.hasError)
|
||||
{
|
||||
logExceptions(resolveReport)
|
||||
println(Set(resolveReport.getAllProblemMessages.toArray: _*).mkString(System.getProperty("line.separator")))
|
||||
throw new BootException("Error retrieving required libraries")
|
||||
}
|
||||
}
|
||||
/** Exceptions are logged to the update log file. */
|
||||
private def logExceptions(report: ResolveReport)
|
||||
{
|
||||
for(unresolved <- report.getUnresolvedDependencies)
|
||||
{
|
||||
val problem = unresolved.getProblem
|
||||
if(problem != null)
|
||||
problem.printStackTrace(logWriter)
|
||||
}
|
||||
}
|
||||
/** Retrieves resolved dependencies using the given target to determine the location to retrieve to. */
|
||||
private def retrieve(settings: IvySettings, eventManager: EventManager, module: ModuleDescriptor, target: UpdateTarget.Value)
|
||||
{
|
||||
val retrieveOptions = new RetrieveOptions
|
||||
val retrieveEngine = new RetrieveEngine(settings, eventManager)
|
||||
val pattern =
|
||||
target match
|
||||
{
|
||||
// see BuildConfiguration
|
||||
case UpdateSbt => sbtRetrievePattern(sbtVersion)
|
||||
case UpdateScala => scalaRetrievePattern
|
||||
}
|
||||
retrieveEngine.retrieve(module.getModuleRevisionId, pattern, retrieveOptions);
|
||||
}
|
||||
/** Add the scala tools repositories and a URL resolver to download sbt from the Google code project.*/
|
||||
private def addResolvers(settings: IvySettings, scalaVersion: String, target: UpdateTarget.Value)
|
||||
{
|
||||
val newDefault = new ChainResolver
|
||||
newDefault.setName("redefined-public")
|
||||
newDefault.add(localResolver(settings.getDefaultIvyUserDir.getAbsolutePath))
|
||||
newDefault.add(mavenLocal)
|
||||
newDefault.add(mavenMainResolver)
|
||||
target match
|
||||
{
|
||||
case UpdateSbt =>
|
||||
newDefault.add(sbtResolver(scalaVersion))
|
||||
case UpdateScala =>
|
||||
newDefault.add(mavenResolver("Scala-Tools Maven2 Repository", "http://scala-tools.org/repo-releases"))
|
||||
newDefault.add(scalaSnapshots(scalaVersion))
|
||||
}
|
||||
onDefaultRepositoryCacheManager(settings)(_.setUseOrigin(true))
|
||||
settings.addResolver(newDefault)
|
||||
settings.setDefaultResolver(newDefault.getName)
|
||||
}
|
||||
private def onDefaultRepositoryCacheManager(settings: IvySettings)(f: DefaultRepositoryCacheManager => Unit)
|
||||
{
|
||||
settings.getDefaultRepositoryCacheManager match
|
||||
{
|
||||
case manager: DefaultRepositoryCacheManager => f(manager)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
/** Uses the pattern defined in BuildConfiguration to download sbt from Google code.*/
|
||||
private def sbtResolver(scalaVersion: String) =
|
||||
{
|
||||
val pattern = sbtResolverPattern(scalaVersion)
|
||||
val resolver = new URLResolver
|
||||
resolver.setName("Sbt Repository")
|
||||
resolver.addIvyPattern(pattern)
|
||||
resolver.addArtifactPattern(pattern)
|
||||
resolver
|
||||
}
|
||||
private def mavenLocal = mavenResolver("Maven2 Local", "file://" + System.getProperty("user.home") + "/.m2/repository/")
|
||||
/** Creates a maven-style resolver.*/
|
||||
private def mavenResolver(name: String, root: String) =
|
||||
{
|
||||
val resolver = defaultMavenResolver(name)
|
||||
resolver.setRoot(root)
|
||||
resolver
|
||||
}
|
||||
/** Creates a resolver for Maven Central.*/
|
||||
private def mavenMainResolver = defaultMavenResolver("Maven Central")
|
||||
/** Creates a maven-style resolver with the default root.*/
|
||||
private def defaultMavenResolver(name: String) =
|
||||
{
|
||||
val resolver = new IBiblioResolver
|
||||
resolver.setName(name)
|
||||
resolver.setM2compatible(true)
|
||||
resolver
|
||||
}
|
||||
private val SnapshotPattern = """(\d+).(\d+).(\d+)-(\d{8})\.(\d{6})-(\d+|\+)""".r.pattern
|
||||
private def scalaSnapshots(scalaVersion: String) =
|
||||
{
|
||||
val m = SnapshotPattern.matcher(scalaVersion)
|
||||
if(m.matches)
|
||||
{
|
||||
val base = Seq(1,2,3).map(m.group).mkString(".")
|
||||
val pattern = "http://scala-tools.org/repo-snapshots/[organization]/[module]/" + base + "-SNAPSHOT/[artifact]-[revision].[ext]"
|
||||
|
||||
val resolver = new URLResolver
|
||||
resolver.setName("Scala Tools Snapshots")
|
||||
resolver.setM2compatible(true)
|
||||
resolver.addArtifactPattern(pattern)
|
||||
resolver
|
||||
}
|
||||
else
|
||||
mavenResolver("Scala-Tools Maven2 Snapshots Repository", "http://scala-tools.org/repo-snapshots")
|
||||
}
|
||||
private def localResolver(ivyUserDirectory: String) =
|
||||
{
|
||||
val localIvyRoot = ivyUserDirectory + "/local"
|
||||
val artifactPattern = localIvyRoot + "/" + LocalArtifactPattern
|
||||
val ivyPattern = localIvyRoot + "/" + LocalIvyPattern
|
||||
val resolver = new FileSystemResolver
|
||||
resolver.setName(LocalIvyName)
|
||||
resolver.addIvyPattern(ivyPattern)
|
||||
resolver.addArtifactPattern(artifactPattern)
|
||||
resolver
|
||||
}
|
||||
/** Logs the given message to a file and to the console. */
|
||||
private def log(msg: String) =
|
||||
{
|
||||
try { logWriter.println(msg) }
|
||||
catch { case e: Exception => System.err.println("Error writing to update log file: " + e.toString) }
|
||||
println(msg)
|
||||
}
|
||||
}
|
||||
/** A custom logger for Ivy to ignore the messages about not finding classes
|
||||
* intentionally filtered using proguard. */
|
||||
private final class SbtIvyLogger(logWriter: PrintWriter) extends DefaultMessageLogger(Message.MSG_INFO) with NotNull
|
||||
{
|
||||
private val ignorePrefix = "impossible to define"
|
||||
override def log(msg: String, level: Int)
|
||||
{
|
||||
logWriter.println(msg)
|
||||
if(level <= getLevel && msg != null && !msg.startsWith(ignorePrefix))
|
||||
System.out.println(msg)
|
||||
}
|
||||
override def rawlog(msg: String, level: Int) { log(msg, level) }
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
<!-- to be replace by inline ModuleConfigurations when 0.5.5 is released -->
|
||||
<ivysettings>
|
||||
<settings defaultResolver="sbt-chain"/>
|
||||
<include url="${ivy.default.settings.dir}/ivysettings-public.xml"/>
|
||||
<include url="${ivy.default.settings.dir}/ivysettings-local.xml"/>
|
||||
<resolvers>
|
||||
<ibiblio name="scala-tools" m2compatible="true" root="http://scala-tools.org/repo-releases/"/>
|
||||
<url name="scala-snapshots" m2compatible="true" >
|
||||
<artifact pattern="http://scala-tools.org/repo-snapshots/[organization]/[module]/2.8.0-SNAPSHOT/[artifact]-[revision].[ext]" />
|
||||
</url>
|
||||
<chain name="sbt-chain" returnFirst="true" checkmodified="true">
|
||||
<resolver ref="local"/>
|
||||
<resolver ref="public"/>
|
||||
<resolver ref="scala-tools"/>
|
||||
</chain>
|
||||
</resolvers>
|
||||
<modules>
|
||||
<module organisation="org.scala-lang" revision="2.8.0-.*" resolver="scala-snapshots"/>
|
||||
</modules>
|
||||
</ivysettings>
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#Project properties
|
||||
#Tue Sep 29 20:46:06 EDT 2009
|
||||
project.organization=sbt
|
||||
#Thu Oct 01 17:51:28 EDT 2009
|
||||
project.organization=org.scala-tools.sbt
|
||||
project.name=Simple Build Tool
|
||||
sbt.version=0.5.5
|
||||
project.version=0.5.6-SNAPSHOT
|
||||
scala.version=2.7.2
|
||||
scala.version=2.7.5
|
||||
|
|
|
|||
|
|
@ -1,217 +0,0 @@
|
|||
import sbt._
|
||||
|
||||
import java.io.File
|
||||
import scala.xml.NodeSeq
|
||||
|
||||
/** Support for compiling sbt across multiple versions of Scala. The scala compiler is run in a
|
||||
* separate JVM and no partial compilation is done.*/
|
||||
abstract class CrossCompileProject extends BasicScalaProject with MavenStyleScalaPaths
|
||||
{
|
||||
/* The base configuration names for the versions of Scala*/
|
||||
private val version2_7_2 = "2.7.2"
|
||||
private val version2_7_3 = "2.7.3"
|
||||
private val version2_7_4 = "2.7.4"
|
||||
private val version2_7_5 = "2.7.5"
|
||||
private val version2_7_6 = "2.7.6"
|
||||
private val version2_8_0 = "2.8.0-20090929.004247-+"
|
||||
private val base = "base"
|
||||
|
||||
/* The configurations for the versions of Scala.*/
|
||||
private val conf_2_7_2 = config(version2_7_2)
|
||||
private val conf_2_7_3 = config(version2_7_3)
|
||||
private val conf_2_7_4 = config(version2_7_4)
|
||||
private val conf_2_7_5 = config(version2_7_5)
|
||||
private val conf_2_7_6 = config(version2_7_6)
|
||||
private val conf_2_8_0 = config(version2_8_0)
|
||||
private val conf_base = config(base)
|
||||
// the list of all configurations cross-compile supports
|
||||
private val allConfigurations = conf_2_7_2 :: conf_2_7_3 :: conf_2_7_4 :: conf_2_7_5 :: conf_2_7_6 :: conf_2_8_0 :: Nil
|
||||
// the list of configurations to actually build against
|
||||
private val buildConfigurations = allConfigurations//conf_2_7_2 :: conf_2_8_0 :: Nil//conf_2_7_2 :: conf_2_7_3 :: conf_2_7_4 :: conf_2_7_5 :: Nil
|
||||
// the configuration to use for normal development (when cross-building is not done)
|
||||
private def developmentVersion = buildConfigurations.first
|
||||
|
||||
/* Methods to derive the configuration name from the base name 'v'.*/
|
||||
private def optional(v: Configuration) = config("optional-" + v.toString)
|
||||
private def scalac(v: Configuration) = config("scalac-" + v.toString)
|
||||
private def sbt(v: Configuration) = config("sbt_" + v.toString)
|
||||
private def depConf(v: Configuration) = v.toString + "->default"
|
||||
|
||||
// =========== Cross-compilation across scala versions ===========
|
||||
|
||||
// The dependencies that should go in each configuration are:
|
||||
// base Required dependencies that are the same across all scala versions.
|
||||
// <version> Required dependencies to use with Scala <version>
|
||||
// optional-base Optional dependencies that are the same for all scala versions
|
||||
// optional-<version> Optional dependencies to use with Scala <version>
|
||||
// compile Used for normal development, it should extend a specific <version> and optional-<version>
|
||||
// scalac-<version> The scala compiler for Scala <version>
|
||||
// There should be a jar publication for each version of scala. The artifact should be named sbt_<version>.
|
||||
override def ivyXML =
|
||||
(<configurations>
|
||||
<conf name={conf_base.toString}/>
|
||||
<conf name={optional(conf_base).toString}/>
|
||||
{ variableConfigurations }
|
||||
<!-- The configuration used for normal development (actions other than cross-*) -->
|
||||
<conf name="default" extends={developmentVersion + "," + optional(developmentVersion).toString} visibility="private"/>
|
||||
</configurations>
|
||||
<publications>
|
||||
{ publications }
|
||||
</publications>
|
||||
<dependencies>
|
||||
<!-- Dependencies that are the same across all Scala versions -->
|
||||
<dependency org="org.apache.ivy" name="ivy" rev="2.0.0" transitive="false" conf={depConf(conf_base)}/>
|
||||
<dependency org="com.jcraft" name="jsch" rev="0.1.31" transitive="false" conf={depConf(conf_base)}/>
|
||||
<dependency org="org.mortbay.jetty" name="jetty" rev="6.1.14" transitive="true" conf={depConf(optional(conf_base))}/>
|
||||
|
||||
<!-- the dependencies that are different depending on the version of Scala -->
|
||||
{ variableDependencies(conf_2_7_2, /*ScalaTest*/"0.9.3", /*Specs*/"1.4.0", false) }
|
||||
{ variableDependencies(conf_2_7_3, /*ScalaTest*/"0.9.4", /*Specs*/"1.4.3", true) }
|
||||
{ variableDependencies(conf_2_7_4, /*ScalaTest*/"0.9.5", /*Specs*/"1.4.3", true) }
|
||||
{ variableDependencies(conf_2_7_5, /*ScalaTest*/"0.9.5", /*Specs*/"1.4.3", true) }
|
||||
{ variableDependencies(conf_2_7_6, /*ScalaTest*/"0.9.5", /*Specs*/"1.4.3", true) }
|
||||
{ variableDependencies(conf_2_8_0, /*ScalaTest*/"0.9.5", /*Specs*/"1.4.3", true) }
|
||||
</dependencies>)
|
||||
|
||||
/** Creates a publication (an 'artifact' element) for each Scala version */
|
||||
private def publications: NodeSeq =
|
||||
{
|
||||
for(conf <- buildConfigurations) yield
|
||||
<artifact name={sbt(conf).toString} conf={conf.toString}/>
|
||||
}
|
||||
/** Creates the main, optional, and scalac configurations for each Scala version*/
|
||||
private def variableConfigurations: NodeSeq =
|
||||
{
|
||||
buildConfigurations flatMap
|
||||
{ conf =>
|
||||
scalaComment(conf) ++
|
||||
(<conf name={conf.toString} extends={conf_base.toString}/>
|
||||
<conf name={optional(conf).toString} extends={optional(conf_base).toString}/>
|
||||
<conf name={scalac(conf).toString} visibility="private"/>)
|
||||
}
|
||||
}
|
||||
/** Defines the dependencies for the given version of Scala, ScalaTest, and Specs. If uniformTestOrg is true,
|
||||
* the 'org.scala-tools.testing' organization is used. Otherwise, 'org.' is prefixed to the module name. */
|
||||
private def variableDependencies(scalaVersion: Configuration, scalaTestVersion: String, specsVersion: String, uniformTestOrg: Boolean) =
|
||||
{
|
||||
if(buildConfigurations.contains(scalaVersion))
|
||||
{
|
||||
scalaComment(scalaVersion) ++
|
||||
{
|
||||
if(scalaVersion eq conf_2_8_0)
|
||||
Nil
|
||||
else
|
||||
{
|
||||
testDependency("scalatest", scalaTestVersion, uniformTestOrg, scalaVersion) ++
|
||||
testDependency("specs", specsVersion, uniformTestOrg, scalaVersion) ++
|
||||
testDependency("scalacheck", "1.5", false, scalaVersion)
|
||||
}
|
||||
} ++
|
||||
scalaDependency("scala-compiler", scalaVersion) ++ scalaDependency("scala-library", scalaVersion) ++
|
||||
{
|
||||
if(scalaVersion == conf_2_8_0)
|
||||
<dependency org="jline" name="jline" rev="0.9.94" transitive="false" conf={depConf(conf_2_8_0)}/>
|
||||
else
|
||||
NodeSeq.Empty
|
||||
}
|
||||
}
|
||||
else
|
||||
Nil
|
||||
}
|
||||
private def scalaDependency(name: String, scalaVersion: Configuration) =
|
||||
<dependency org="org.scala-lang" name={name} rev={scalaVersion.toString} conf={depConf(scalac(scalaVersion))}/>
|
||||
|
||||
/** Creates a comment containing the version of Scala*/
|
||||
private def scalaComment(scalaVersion: Configuration) = scala.xml.Comment("Scala " + scalaVersion)
|
||||
/** Creates a dependency element for a test. See 'testOrg' for a description of uniformTestOrg.*/
|
||||
|
||||
private def testDependency(name: String, version: String, uniformTestOrg: Boolean, baseConf: Configuration) =
|
||||
<dependency org={testOrg(name, uniformTestOrg)} name={name} rev={version} transitive="false" conf={depConf(optional(baseConf))}/>
|
||||
|
||||
/** Returns the organization for the given test library. If uniform is true,
|
||||
* the 'org.scala-tools.testing' organization is used. Otherwise, 'org.' is prefixed to the module name.*/
|
||||
private def testOrg(name: String, uniform: Boolean) =
|
||||
if(uniform) "org.scala-tools.testing"
|
||||
else "org." + name
|
||||
|
||||
/** Disable filtering Scala jars from dependency management, because we need them and are putting them
|
||||
* in custom configurations and are using them in a separate process than sbt runs in.*/
|
||||
override def filterScalaJars = false
|
||||
|
||||
/** The lib directory is now only for building using the 'build' script.*/
|
||||
override def unmanagedClasspath = path("ignore_lib_directory")
|
||||
/** When cross-compiling, replace mainCompilePath with the classes directory for the version being compiled.*/
|
||||
override def fullUnmanagedClasspath(config: Configuration) =
|
||||
if( (Configurations.Default :: Configurations.defaultMavenConfigurations) contains config)
|
||||
super.fullUnmanagedClasspath(config)
|
||||
else
|
||||
classesPath(config) +++ mainResourcesPath
|
||||
|
||||
// include the optional-<version> dependencies as well as the ones common across all scala versions
|
||||
def optionalClasspath(version: Configuration) = fullClasspath(optional(version)) +++ super.optionalClasspath
|
||||
|
||||
private val CompilerMainClass = "scala.tools.nsc.Main"
|
||||
// use a publish configuration that publishes the 'base' + all <version> configurations (base is required because
|
||||
// the <version> configurations extend it)
|
||||
private val conf = new DefaultPublishConfiguration("local", "release")
|
||||
{
|
||||
override def configurations: Option[Iterable[Configuration]] = Some(config(base) :: buildConfigurations)
|
||||
}
|
||||
// the actions for cross-version packaging and publishing
|
||||
lazy val crossPackage = buildConfigurations.map(packageForScala)
|
||||
lazy val crossDeliverLocal = deliverTask(conf, updateOptions) dependsOn(crossPackage : _*)
|
||||
lazy val crossPublishLocal = publishTask(conf, updateOptions) dependsOn(crossDeliverLocal)
|
||||
// Creates a task that produces a packaged sbt compiled against Scala scalaVersion.
|
||||
// The jar is named 'sbt_<scala-version>-<sbt-version>.jar'
|
||||
private def packageForScala(scalaVersion: Configuration) =
|
||||
{
|
||||
val classes = classesPath(scalaVersion) ** "*"
|
||||
val jarName = crossJarName(scalaVersion)
|
||||
val packageActionName = crossActionName("package", scalaVersion)
|
||||
val compileAction = compileForScala(scalaVersion) named(crossActionName("compile", scalaVersion))
|
||||
packageTask(classes +++ mainResources, outputPath, jarName, packageOptions) dependsOn(compileAction) named(packageActionName)
|
||||
}
|
||||
private def crossActionName(base: String, scalaVersion: Configuration) = base + " [ " + scalaVersion.toString + " ] "
|
||||
private def crossJarName(scalaVersion: Configuration) = sbt(scalaVersion) + "-" + version.toString + ".jar"
|
||||
// This creates a task that compiles sbt against the given version of scala. Classes are put in classes-<scalaVersion>.
|
||||
private def compileForScala(version: Configuration)=
|
||||
{
|
||||
def filteredSources =
|
||||
if(version eq conf_2_8_0) // cannot compile against test libraries currently
|
||||
Path.lazyPathFinder { mainSources.get.filter(!_.asFile.getName.endsWith("TestFrameworkImpl.scala")) }
|
||||
else
|
||||
mainSources
|
||||
task
|
||||
{
|
||||
val classes = classesPath(version)
|
||||
val toClean = (outputPath / crossJarName(version)) +++ (classes ** "*")
|
||||
val setupResult =
|
||||
FileUtilities.clean(toClean.get, true, log) orElse
|
||||
FileUtilities.createDirectory(classes, log)
|
||||
for(err <- setupResult) log.error(err)
|
||||
// the classpath containing the scalac compiler
|
||||
val compilerClasspath = concatPaths(fullClasspath(scalac(version)))
|
||||
|
||||
// The libraries to compile sbt against
|
||||
val classpath = fullClasspath(version) +++ optionalClasspath(version)
|
||||
val sources: List[String] = pathListStrings(filteredSources)
|
||||
val compilerOptions = List("-cp", concatPaths(classpath), "-d", classes.toString)
|
||||
val compilerArguments: List[String] = compilerOptions ::: sources
|
||||
|
||||
// the compiler classpath has to be appended to the boot classpath to work properly
|
||||
val allArguments = "-Xmx512M" :: ("-Xbootclasspath/a:" + compilerClasspath) :: CompilerMainClass :: compilerArguments
|
||||
log.debug("Running external compiler with command: java " + allArguments.mkString(" "))
|
||||
val start = System.currentTimeMillis
|
||||
val exitValue = Process("java", allArguments) ! log
|
||||
val stop = System.currentTimeMillis
|
||||
log.info("Compiled sbt with Scala " + version.name + " in " + ((stop-start)/1000.0) + " s")
|
||||
if(exitValue == 0)
|
||||
None
|
||||
else
|
||||
Some("Nonzero exit value (" + exitValue + ") when calling scalac " + version + " with options: \n" + compilerOptions.mkString(" "))
|
||||
}
|
||||
}
|
||||
private def concatPaths(p: PathFinder): String = Path.makeString(p.get)
|
||||
private def pathListStrings(p: PathFinder): List[String] = p.get.map(_.absolutePath).toList
|
||||
private def classesPath(scalaVersion: Configuration) = ("target" / ("classes-" + scalaVersion.toString)) ##
|
||||
}
|
||||
|
|
@ -1,151 +0,0 @@
|
|||
import sbt._
|
||||
|
||||
import LoaderProject._
|
||||
import java.io.File
|
||||
|
||||
// a project for the sbt launcher
|
||||
// the main content of this project definition is setting up and running proguard
|
||||
// to combine and compact all dependencies into a single jar
|
||||
protected/* removes the ambiguity as to which project is the entry point by making this class non-public*/
|
||||
class LoaderProject(info: ProjectInfo) extends DefaultProject(info)
|
||||
{
|
||||
val mainClassName = "sbt.boot.Boot"
|
||||
val baseName = "sbt-launcher"
|
||||
val proguardConfigurationPath: Path = outputPath / "proguard.pro"
|
||||
lazy val outputJar: Path = rootProject.outputPath / (baseName + "-" + version + ".jar")
|
||||
def rootProjectDirectory = rootProject.info.projectPath
|
||||
|
||||
override lazy val release = task { None }
|
||||
override def mainClass = Some(mainClassName)
|
||||
override def defaultJarBaseName = baseName + "-" + version.toString
|
||||
|
||||
/****** Resources *****/
|
||||
def extraResources = descendents(info.projectPath / "licenses", "*") +++ "LICENSE" +++ "NOTICE"
|
||||
override def mainResources = super.mainResources +++ extraResources
|
||||
|
||||
/****** Dependencies *******/
|
||||
val defaultConfig = config("default")
|
||||
val toolsConfig = config("tools")
|
||||
val ivy = "org.apache.ivy" % "ivy" % "2.0.0"
|
||||
val proguardJar = "net.sf.proguard" % "proguard" % "4.3" % "tools->default"
|
||||
|
||||
/******** Proguard *******/
|
||||
lazy val proguard = proguardTask dependsOn(`package`, writeProguardConfiguration) describedAs(ProguardDescription)
|
||||
lazy val writeProguardConfiguration = writeProguardConfigurationTask dependsOn `package` describedAs WriteProguardDescription
|
||||
|
||||
private def proguardTask =
|
||||
task
|
||||
{
|
||||
FileUtilities.clean(outputJar :: Nil, log)
|
||||
val proguardClasspath = managedClasspath(toolsConfig)
|
||||
val proguardClasspathString = Path.makeString(proguardClasspath.get)
|
||||
val configFile = proguardConfigurationPath.asFile.getAbsolutePath
|
||||
val exitValue = Process("java", List("-Xmx128M", "-cp", proguardClasspathString, "proguard.ProGuard", "@" + configFile)) ! log
|
||||
if(exitValue == 0) None else Some("Proguard failed with nonzero exit code (" + exitValue + ")")
|
||||
}
|
||||
private def writeProguardConfigurationTask =
|
||||
task
|
||||
{
|
||||
// these are classes that need to be explicitly kept because they are loaded reflectively
|
||||
val ivyKeepResolvers =
|
||||
"org.apache.ivy.plugins.resolver.URLResolver" ::
|
||||
"org.apache.ivy.plugins.resolver.IBiblioResolver" ::
|
||||
Nil
|
||||
// the template for the proguard configuration file
|
||||
val outTemplate = """
|
||||
|-dontoptimize
|
||||
|-dontobfuscate
|
||||
|-dontnote
|
||||
|-dontwarn
|
||||
|-libraryjars %s
|
||||
|-injars %s(!META-INF/**,!fr/**,!**/antlib.xml,!**/*.png)
|
||||
|-injars %s(!META-INF/**)
|
||||
|%s
|
||||
|-outjars %s
|
||||
|-ignorewarnings
|
||||
|%s
|
||||
|%s
|
||||
|-keep public class %s {
|
||||
| public static void main(java.lang.String[]);
|
||||
|}"""
|
||||
|
||||
val defaultJar = mkpath((outputPath / defaultJarName).asFile)
|
||||
log.debug("proguard configuration using main jar " + defaultJar)
|
||||
val ivyKeepOptions = ivyKeepResolvers.map("-keep public class " + _ + allPublic).mkString("\n")
|
||||
val runtimeClasspath = runClasspath.get.map(_.asFile).toList
|
||||
val jlineJars = runtimeClasspath.filter(isJLineJar)
|
||||
val externalDependencies = (mainCompileConditional.analysis.allExternals).map(_.getAbsoluteFile).filter(_.getName.endsWith(".jar"))
|
||||
log.debug("proguard configuration external dependencies: \n\t" + externalDependencies.mkString("\n\t"))
|
||||
// partition jars from the external jar dependencies of this project by whether they are located in the project directory
|
||||
// if they are, they are specified with -injars, otherwise they are specified with -libraryjars
|
||||
val (externalJars, libraryJars) = externalDependencies.toList.partition(jar => Path.relativize(rootProjectDirectory, jar).isDefined)
|
||||
log.debug("proguard configuration library jars locations: " + libraryJars.mkString(", "))
|
||||
// pull out Ivy in order to exclude resources inside
|
||||
val (ivyJars, externalJarsNoIvy) = externalJars.partition(_.getName.startsWith("ivy"))
|
||||
log.debug("proguard configuration ivy jar location: " + ivyJars.mkString(", "))
|
||||
// the loader uses JLine, so there is a dependency on the compiler (because JLine is distributed with the compiler,
|
||||
// it finds the JLine classes from the compiler jar instead of the jline jar on the classpath), but we don't want to
|
||||
// include the version of JLine from the compiler.
|
||||
val includeExternalJars = externalJarsNoIvy.filter(jar => !isJarX(jar, "scala-compiler"))
|
||||
// exclude properties files and manifests from scala-library jar
|
||||
val inJars = (defaultJar :: includeExternalJars.map(f => mkpath(f) + "(!META-INF/**,!*.properties)")).map("-injars " + _).mkString("\n")
|
||||
|
||||
withJar(ivyJars, "Ivy") { ivyJar =>
|
||||
withJar(jlineJars, "JLine") { jlineJar =>
|
||||
val proguardConfiguration =
|
||||
outTemplate.stripMargin.format(mkpaths(libraryJars),
|
||||
mkpath(ivyJar), mkpath(jlineJar),
|
||||
inJars, mkpath(outputJar.asFile), ivyKeepOptions, keepJLine, mainClassName)
|
||||
log.debug("Proguard configuration written to " + proguardConfigurationPath)
|
||||
FileUtilities.write(proguardConfigurationPath.asFile, proguardConfiguration, log)
|
||||
}
|
||||
}
|
||||
}
|
||||
private def mkpaths(f: Seq[File]) = f.map(mkpath).mkString(File.separator)
|
||||
private def mkpath(f: File) = '\"' + f.getAbsolutePath + '\"'
|
||||
private def withJar(files: List[File], name: String)(f: File => Option[String]): Option[String] =
|
||||
files match
|
||||
{
|
||||
case Nil => Some(name + " not present (try running update)")
|
||||
case jar :: _ => f(jar)
|
||||
}
|
||||
private def isJLineJar(file: File) = isJarX(file, "jline")
|
||||
private def isJarX(file: File, x: String) =
|
||||
{
|
||||
val name = file.getName
|
||||
name.startsWith(x) && name.endsWith(".jar")
|
||||
}
|
||||
// class body declaration for proguard that keeps all public members
|
||||
private val allPublic = " {\n public * ;\n}"
|
||||
|
||||
private val keepJLine =
|
||||
"""
|
||||
|-keep public class jline.** {
|
||||
| public protected *;
|
||||
|}
|
||||
""".stripMargin
|
||||
|
||||
def managedSrc = path("src_managed")
|
||||
def defaultVersionPath = managedSrc / "DefaultVersions.scala"
|
||||
override def mainSourceRoots = super.mainSourceRoots +++ managedSrc
|
||||
override def compileAction = super.compileAction dependsOn(versionProperties)
|
||||
override def cleanAction = cleanTask(outputPath +++ managedSrc, cleanOptions)
|
||||
lazy val versionProperties = task { writeVersionProperties(projectVersion.value.toString, "2.7.5") }//scalaVersion.value.toString) }
|
||||
def writeVersionProperties(sbtVersion: String, scalaVersion: String) =
|
||||
FileUtilities.write(defaultVersionPath.asFile, defaultVersions(sbtVersion, scalaVersion), log)
|
||||
|
||||
def defaultVersions(sbtVersion: String, scalaVersion: String) =
|
||||
"""
|
||||
package sbt.boot
|
||||
object DefaultVersions
|
||||
{
|
||||
val Sbt = "%s"
|
||||
val Scala = "%s"
|
||||
}
|
||||
""".format(sbtVersion, scalaVersion)
|
||||
}
|
||||
object LoaderProject
|
||||
{
|
||||
val ProguardDescription = "Produces the final compacted jar that contains only the minimum classes needed using proguard."
|
||||
val WriteProguardDescription = "Creates the configuration file to use with proguard."
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2009 Mark Harrah
|
||||
*/
|
||||
import sbt._
|
||||
/*import sbt._
|
||||
|
||||
import java.io.File
|
||||
|
||||
|
|
@ -93,4 +93,4 @@ package sbt {
|
|||
implicit def elemToPB(command: scala.xml.Elem): ProcessBuilder =
|
||||
impl.CommandParser.parse(command.text.trim).fold(error, Function.tupled(Process.apply))
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
|
@ -5,33 +5,91 @@ import sbt._
|
|||
|
||||
import java.io.File
|
||||
|
||||
class SbtProject(info: ProjectInfo) extends ParentProject(info) with ReleaseProject
|
||||
class SbtProject(info: ProjectInfo) extends DefaultProject(info) //with test.SbtScripted
|
||||
{
|
||||
// Launcher sub project.
|
||||
lazy val boot = project("boot", "Simple Build Tool Loader", new LoaderProject(_))
|
||||
// Main builder sub project
|
||||
lazy val main = project(info.projectPath, "Simple Build Tool", new MainProject(_))
|
||||
// One-shot build for users building from trunk
|
||||
lazy val fullBuild = task { None } dependsOn(boot.proguard, main.crossPublishLocal) describedAs
|
||||
"Builds the loader and builds main sbt against all supported versions of Scala and installs to the local repository."
|
||||
|
||||
override def shouldCheckOutputDirectories = false
|
||||
override def baseUpdateOptions = QuietUpdate :: Nil
|
||||
|
||||
//override def parallelExecution = true
|
||||
override def deliverLocalAction = noAction
|
||||
private def noAction = task { None }
|
||||
override def publishLocalAction = noAction
|
||||
}
|
||||
|
||||
protected class MainProject(val info: ProjectInfo) extends CrossCompileProject with test.SbtScripted
|
||||
{
|
||||
override def defaultJarBaseName = "sbt_" + version.toString
|
||||
/** Additional resources to include in the produced jar.*/
|
||||
def extraResources = descendents(info.projectPath / "licenses", "*") +++ "LICENSE" +++ "NOTICE"
|
||||
override def mainResources = super.mainResources +++ extraResources
|
||||
override def mainClass = Some("sbt.Main")
|
||||
override def testOptions = ExcludeTests("sbt.ReflectiveSpecification" :: "sbt.ProcessSpecification" :: Nil) :: super.testOptions.toList
|
||||
override def scriptedDependencies = testCompile :: `package` :: Nil
|
||||
override lazy val release = task { None }
|
||||
override def normalizedName = "sbt"
|
||||
//override def scriptedDependencies = testCompile :: `package` :: Nil
|
||||
|
||||
def specificSnapshotRepo =
|
||||
Resolver.url("scala-nightly") artifacts("http://scala-tools.org/repo-snapshots/[organization]/[module]/2.8.0-SNAPSHOT/[artifact]-[revision].[ext]") mavenStyle()
|
||||
val nightlyScala = ModuleConfiguration("org.scala-lang", "*", "2.8.0-.*", specificSnapshotRepo)
|
||||
|
||||
override def compileOptions = Nil
|
||||
|
||||
def scalaVersionString = ScalaVersion.current.getOrElse(scalaVersion.value)
|
||||
override def mainSources =
|
||||
{
|
||||
if(scalaVersionString == Version2_8_0)
|
||||
Path.lazyPathFinder( super.mainSources.get.filter( !_.asFile.getName.endsWith("TestFrameworkImpl.scala") ))
|
||||
else
|
||||
super.mainSources
|
||||
}
|
||||
|
||||
override def useDefaultConfigurations = false
|
||||
val default = Configurations.Default
|
||||
val optional = Configurations.Optional
|
||||
val provided = Configurations.Provided
|
||||
|
||||
/* The base configuration names for the versions of Scala*/
|
||||
private val Version2_7_2 = "2.7.2"
|
||||
private val Version2_7_3 = "2.7.3"
|
||||
private val Version2_7_4 = "2.7.4"
|
||||
private val Version2_7_5 = "2.7.5"
|
||||
private val Version2_7_6 = "2.7.6"
|
||||
private val Version2_8_0 = "2.8.0-20090929.004247-+"
|
||||
// the list of all supported versions
|
||||
private def allVersions = Version2_7_2 :: Version2_7_3 :: Version2_7_4 :: Version2_7_5 :: Version2_7_6 :: Version2_8_0 :: Nil
|
||||
|
||||
override def crossScalaVersions = Set(Version2_7_2, Version2_7_5)//, Version2_8_0)
|
||||
|
||||
val ivy = "org.apache.ivy" % "ivy" % "2.0.0" intransitive()
|
||||
val jsch = "com.jcraft" % "jsch" % "0.1.31" intransitive()
|
||||
val jetty = "org.mortbay.jetty" % "jetty" % "6.1.14" % "optional"
|
||||
|
||||
// xsbt components
|
||||
val xsbti = "org.scala-tools.sbt" % "launcher-interface" % "0.7.0_13" % "provided"
|
||||
val compiler = "org.scala-tools.sbt" %% "compile" % "0.7.0_13"
|
||||
|
||||
override def libraryDependencies = super.libraryDependencies ++ getDependencies(scalaVersionString)
|
||||
|
||||
def getDependencies(scalaVersion: String) =
|
||||
scalaVersion match
|
||||
{
|
||||
case Version2_7_2 => variableDependencies(false, scalaVersion, /*ScalaTest*/"0.9.3", /*Specs*/"1.4.0", false)
|
||||
case Version2_7_3 => variableDependencies(false, scalaVersion, /*ScalaTest*/"0.9.4", /*Specs*/"1.4.3", true)
|
||||
case Version2_7_4 => variableDependencies(false, scalaVersion, /*ScalaTest*/"0.9.5", /*Specs*/"1.4.3", true)
|
||||
case Version2_7_5 => variableDependencies(false, scalaVersion, /*ScalaTest*/"0.9.5", /*Specs*/"1.4.3", true)
|
||||
case Version2_7_6 => variableDependencies(false, scalaVersion, /*ScalaTest*/"0.9.5", /*Specs*/"1.4.3", true)
|
||||
case Version2_8_0 => variableDependencies(true, scalaVersion, /*ScalaTest*/"0.9.5", /*Specs*/"1.4.3", true)
|
||||
case _ => error("Unsupported Scala version: " + scalaVersion)
|
||||
}
|
||||
|
||||
/** Defines the dependencies for the given version of Scala, ScalaTest, and Specs. If uniformTestOrg is true,
|
||||
* the 'org.scala-tools.testing' organization is used. Otherwise, 'org.' is prefixed to the module name. */
|
||||
private def variableDependencies(is28: Boolean, scalaVersion: String, scalaTestVersion: String, specsVersion: String, uniformTestOrg: Boolean) =
|
||||
{
|
||||
( if(is28) Nil else testDependencies(scalaTestVersion, specsVersion, uniformTestOrg)) ++
|
||||
( if(is28) Seq("jline" % "jline" % "0.9.94" intransitive()) else Nil)
|
||||
}
|
||||
private def testDependencies(scalaTestVersion: String, specsVersion: String, uniformTestOrg: Boolean) =
|
||||
{
|
||||
testDependency("scalatest", scalaTestVersion, uniformTestOrg) ::
|
||||
testDependency("specs", specsVersion, uniformTestOrg) ::
|
||||
testDependency("scalacheck", "1.5", false) ::
|
||||
Nil
|
||||
}
|
||||
|
||||
/** Creates a dependency element for a test. See 'testOrg' for a description of uniformTestOrg.*/
|
||||
|
||||
private def testDependency(name: String, version: String, uniformTestOrg: Boolean) =
|
||||
testOrg(name, uniformTestOrg) % name % version % "optional" intransitive()
|
||||
|
||||
/** Returns the organization for the given test library. If uniform is true,
|
||||
* the 'org.scala-tools.testing' organization is used. Otherwise, 'org.' is prefixed to the module name.*/
|
||||
private def testOrg(name: String, uniform: Boolean) = if(uniform) "org.scala-tools.testing" else "org." + name
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
import sbt._
|
||||
|
||||
class Plugins(info: ProjectInfo) extends PluginDefinition(info)
|
||||
{
|
||||
val scripted = "org.scala-tools.sbt" % "test" % "0.5.3"
|
||||
}
|
||||
|
|
@ -1,246 +0,0 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2008, 2009 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
|
||||
import scala.tools.nsc.{io, plugins, symtab, Global, Phase}
|
||||
import io.{AbstractFile, PlainFile, ZipArchive}
|
||||
import plugins.{Plugin, PluginComponent}
|
||||
import symtab.Flags
|
||||
import scala.collection.mutable.{HashMap, HashSet, Map, Set}
|
||||
|
||||
import java.io.File
|
||||
|
||||
object Analyzer
|
||||
{
|
||||
val PluginName = "sbt-analyzer"
|
||||
val CallbackIDOptionName = "callback:"
|
||||
}
|
||||
class Analyzer(val global: Global) extends Plugin
|
||||
{
|
||||
import global._
|
||||
import Analyzer._
|
||||
|
||||
val name = PluginName
|
||||
val description = "A plugin to find all concrete instances of a given class and extract dependency information."
|
||||
val components = List[PluginComponent](Component)
|
||||
|
||||
private var callbackOption: Option[AnalysisCallback] = None
|
||||
|
||||
override def processOptions(options: List[String], error: String => Unit)
|
||||
{
|
||||
for(option <- options)
|
||||
{
|
||||
if(option.startsWith(CallbackIDOptionName))
|
||||
callbackOption = AnalysisCallback(option.substring(CallbackIDOptionName.length).toInt)
|
||||
else
|
||||
error("Option for sbt analyzer plugin not understood: " + option)
|
||||
}
|
||||
if(callbackOption.isEmpty)
|
||||
error("Callback ID not specified for sbt analyzer plugin.")
|
||||
}
|
||||
|
||||
override val optionsHelp: Option[String] =
|
||||
{
|
||||
val prefix = " -P:" + name + ":"
|
||||
Some(prefix + CallbackIDOptionName + "<callback-id> Set the callback id.\n")
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
// These two templates abuse scope for source compatibility between Scala 2.7.x and 2.8.x so that a single
|
||||
// sbt codebase compiles with both series of versions.
|
||||
// In 2.8.x, PluginComponent.runsAfter has type List[String] and the method runsBefore is defined on
|
||||
// PluginComponent with default value Nil.
|
||||
// In 2.7.x, runsBefore does not exist on PluginComponent and PluginComponent.runsAfter has type String.
|
||||
//
|
||||
// Therefore, in 2.8.x, object runsBefore is shadowed by PluginComponent.runsBefore (which is Nil) and so
|
||||
// afterPhase :: runsBefore
|
||||
// is equivalent to List[String](afterPhase)
|
||||
// In 2.7.x, object runsBefore is not shadowed and so runsAfter has type String.
|
||||
private object runsBefore { def :: (s: String) = s }
|
||||
private abstract class CompatiblePluginComponent(afterPhase: String) extends PluginComponent
|
||||
{
|
||||
override val runsAfter = afterPhase :: runsBefore
|
||||
}
|
||||
/* ================================================== */
|
||||
|
||||
private object Component extends CompatiblePluginComponent("jvm")
|
||||
{
|
||||
val global = Analyzer.this.global
|
||||
val phaseName = Analyzer.this.name
|
||||
def newPhase(prev: Phase) = new AnalyzerPhase(prev)
|
||||
}
|
||||
|
||||
private class AnalyzerPhase(prev: Phase) extends Phase(prev)
|
||||
{
|
||||
def name = Analyzer.this.name
|
||||
def run
|
||||
{
|
||||
val callback = callbackOption.get
|
||||
val projectPath = callback.basePath
|
||||
val projectPathString = Path.basePathString(projectPath).getOrElse({error("Could not determine base path for " + projectPath); ""})
|
||||
def relativize(file: File) = Path.relativize(projectPath, projectPathString, file)
|
||||
|
||||
val outputDir = new File(global.settings.outdir.value)
|
||||
val outputPathOption = relativize(outputDir)
|
||||
if(outputPathOption.isEmpty)
|
||||
error("Output directory " + outputDir.getAbsolutePath + " must be in the project directory.")
|
||||
val outputPath = outputPathOption.get
|
||||
|
||||
val superclassNames = callback.superclassNames.map(newTermName)
|
||||
val superclassesAll =
|
||||
for(name <- superclassNames) yield
|
||||
{
|
||||
try { Some(global.definitions.getClass(name)) }
|
||||
catch { case fe: scala.tools.nsc.FatalError => callback.superclassNotFound(name.toString); None }
|
||||
}
|
||||
val superclasses = superclassesAll.filter(_.isDefined).map(_.get)
|
||||
|
||||
for(unit <- currentRun.units)
|
||||
{
|
||||
// build dependencies structure
|
||||
val sourceFile = unit.source.file.file
|
||||
val sourcePathOption = relativize(sourceFile)
|
||||
if(sourcePathOption.isEmpty)
|
||||
error("Source file " + sourceFile.getAbsolutePath + " must be in the project directory.")
|
||||
val sourcePath = sourcePathOption.get
|
||||
callback.beginSource(sourcePath)
|
||||
for(on <- unit.depends)
|
||||
{
|
||||
val onSource = on.sourceFile
|
||||
if(onSource == null)
|
||||
{
|
||||
classFile(on) match
|
||||
{
|
||||
case Some(f) =>
|
||||
{
|
||||
f match
|
||||
{
|
||||
case ze: ZipArchive#Entry => callback.jarDependency(new File(ze.getArchive.getName), sourcePath)
|
||||
case pf: PlainFile =>
|
||||
{
|
||||
Path.relativize(outputPath, pf.file) match
|
||||
{
|
||||
case None => // dependency is a class file outside of the output directory
|
||||
callback.classDependency(pf.file, sourcePath)
|
||||
case Some(relativeToOutput) => // dependency is a product of a source not included in this compilation
|
||||
callback.productDependency(relativeToOutput, sourcePath)
|
||||
}
|
||||
}
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
case None => ()
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(depPath <- relativize(onSource.file))
|
||||
callback.sourceDependency(depPath, sourcePath)
|
||||
}
|
||||
}
|
||||
|
||||
// find subclasses and modules with main methods
|
||||
for(clazz @ ClassDef(mods, n, _, _) <- unit.body)
|
||||
{
|
||||
val sym = clazz.symbol
|
||||
if(sym != NoSymbol && mods.isPublic && !mods.isAbstract && !mods.isTrait &&
|
||||
!sym.isImplClass && sym.isStatic && !sym.isNestedClass)
|
||||
{
|
||||
val isModule = sym.isModuleClass
|
||||
for(superclass <- superclasses.filter(sym.isSubClass))
|
||||
callback.foundSubclass(sourcePath, sym.fullNameString, superclass.fullNameString, isModule)
|
||||
if(isModule && hasMainMethod(sym))
|
||||
callback.foundApplication(sourcePath, sym.fullNameString)
|
||||
}
|
||||
}
|
||||
|
||||
// build list of generated classes
|
||||
for(iclass <- unit.icode)
|
||||
{
|
||||
val sym = iclass.symbol
|
||||
def addGenerated(separatorRequired: Boolean)
|
||||
{
|
||||
val classPath = pathOfClass(outputPath, sym, separatorRequired)
|
||||
if(classPath.asFile.exists)
|
||||
callback.generatedClass(sourcePath, classPath)
|
||||
}
|
||||
if(sym.isModuleClass && !sym.isImplClass)
|
||||
{
|
||||
if(isTopLevelModule(sym) && sym.linkedClassOfModule == NoSymbol)
|
||||
addGenerated(false)
|
||||
addGenerated(true)
|
||||
}
|
||||
else
|
||||
addGenerated(false)
|
||||
}
|
||||
callback.endSource(sourcePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def classFile(sym: Symbol): Option[AbstractFile] =
|
||||
{
|
||||
import scala.tools.nsc.symtab.Flags
|
||||
val name = sym.fullNameString(java.io.File.separatorChar) + (if (sym.hasFlag(Flags.MODULE)) "$" else "")
|
||||
val entry = classPath.root.find(name, false)
|
||||
if (entry ne null)
|
||||
Some(entry.classFile)
|
||||
else if(isTopLevelModule(sym))
|
||||
{
|
||||
val linked = sym.linkedClassOfModule
|
||||
if(linked == NoSymbol)
|
||||
None
|
||||
else
|
||||
classFile(linked)
|
||||
}
|
||||
else
|
||||
None
|
||||
}
|
||||
|
||||
private def isTopLevelModule(sym: Symbol): Boolean =
|
||||
atPhase (currentRun.picklerPhase.next) {
|
||||
sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass
|
||||
}
|
||||
private def pathOfClass(outputPath: Path, s: Symbol, separatorRequired: Boolean): Path =
|
||||
pathOfClass(outputPath, s, separatorRequired, ".class")
|
||||
private def pathOfClass(outputPath: Path, s: Symbol, separatorRequired: Boolean, postfix: String): Path =
|
||||
{
|
||||
if(s.owner.isPackageClass && s.isPackageClass)
|
||||
packagePath(outputPath, s) / postfix
|
||||
else
|
||||
pathOfClass(outputPath, s.owner.enclClass, true, s.simpleName + (if(separatorRequired) "$" else "") + postfix)
|
||||
}
|
||||
private def packagePath(outputPath: Path, s: Symbol): Path =
|
||||
{
|
||||
if(s.isEmptyPackageClass || s.isRoot)
|
||||
outputPath
|
||||
else
|
||||
packagePath(outputPath, s.owner.enclClass) / s.simpleName.toString
|
||||
}
|
||||
|
||||
private def hasMainMethod(sym: Symbol): Boolean =
|
||||
{
|
||||
val main = sym.info.nonPrivateMember(newTermName("main"))//nme.main)
|
||||
main.tpe match
|
||||
{
|
||||
case OverloadedType(pre, alternatives) => alternatives.exists(alt => isVisible(alt) && isMainType(pre.memberType(alt)))
|
||||
case tpe => isVisible(main) && isMainType(main.owner.thisType.memberType(main))
|
||||
}
|
||||
}
|
||||
private def isVisible(sym: Symbol) = sym != NoSymbol && sym.isPublic && !sym.isDeferred
|
||||
private def isMainType(tpe: Type) =
|
||||
{
|
||||
tpe match
|
||||
{
|
||||
// singleArgument is of type Symbol in 2.8.0 and type Type in 2.7.x
|
||||
case MethodType(List(singleArgument), result) => isUnitType(result) && isStringArray(singleArgument)
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
private lazy val StringArrayType = appliedType(definitions.ArrayClass.typeConstructor, definitions.StringClass.tpe :: Nil)
|
||||
// isStringArray is overloaded to handle the incompatibility between 2.7.x and 2.8.0
|
||||
private def isStringArray(tpe: Type): Boolean = tpe.typeSymbol == StringArrayType.typeSymbol
|
||||
private def isStringArray(sym: Symbol): Boolean = isStringArray(sym.tpe)
|
||||
private def isUnitType(tpe: Type) = tpe.typeSymbol == definitions.UnitClass
|
||||
}
|
||||
|
|
@ -184,7 +184,7 @@ trait ManagedProject extends ClasspathProject
|
|||
implicit def toGroupID(groupID: String): GroupID =
|
||||
{
|
||||
nonEmpty(groupID, "Group ID")
|
||||
new GroupID(groupID, ScalaVersion.currentString)
|
||||
new GroupID(groupID, buildScalaVersion)
|
||||
}
|
||||
implicit def toRepositoryName(name: String): RepositoryName =
|
||||
{
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ private sealed abstract class BasicBuilderProject extends InternalProject with S
|
|||
def dependencyPath = path(DefaultDependencyDirectoryName)
|
||||
def libraries = descendents(dependencyPath, jarFilter)
|
||||
override final def dependencies = Nil
|
||||
|
||||
|
||||
protected final def logInfo(messages: String*): Unit = atInfo { messages.foreach(message => log.info(message)) }
|
||||
protected final def atInfo(action: => Unit)
|
||||
{
|
||||
|
|
@ -31,10 +31,10 @@ private sealed abstract class BasicBuilderProject extends InternalProject with S
|
|||
action
|
||||
log.setLevel(oldLevel)
|
||||
}
|
||||
|
||||
|
||||
def projectClasspath = compilePath +++ libraries +++ sbtJarPath
|
||||
def sbtJarPath = Path.lazyPathFinder { Path.fromFile(FileUtilities.sbtJar) :: Nil }
|
||||
|
||||
|
||||
abstract class BuilderCompileConfiguration extends AbstractCompileConfiguration
|
||||
{
|
||||
def projectPath = info.projectPath
|
||||
|
|
@ -54,11 +54,13 @@ private sealed abstract class BasicBuilderProject extends InternalProject with S
|
|||
def classpath = projectClasspath
|
||||
def analysisPath = outputPath / DefaultMainAnalysisDirectoryName
|
||||
}
|
||||
|
||||
|
||||
def tpe: String
|
||||
|
||||
val definitionCompileConditional = new BuilderCompileConditional(definitionCompileConfiguration, tpe)
|
||||
final class BuilderCompileConditional(config: BuilderCompileConfiguration, tpe: String) extends AbstractCompileConditional(config)
|
||||
import xsbt.{ComponentManager, ScalaInstance}
|
||||
|
||||
val definitionCompileConditional = new BuilderCompileConditional(definitionCompileConfiguration, buildCompiler, tpe)
|
||||
final class BuilderCompileConditional(config: BuilderCompileConfiguration, compiler: xsbt.AnalyzingCompiler, tpe: String) extends AbstractCompileConditional(config, compiler)
|
||||
{
|
||||
type AnalysisType = BuilderCompileAnalysis
|
||||
override protected def constructAnalysis(analysisPath: Path, projectPath: Path, log: Logger) =
|
||||
|
|
@ -94,12 +96,12 @@ private sealed abstract class BasicBuilderProject extends InternalProject with S
|
|||
protected def definitionChanged() {}
|
||||
lazy val compile = compileTask
|
||||
def compileTask = task { definitionCompileConditional.run }
|
||||
|
||||
|
||||
def projectDefinition: Either[String, Option[String]] =
|
||||
{
|
||||
definitionCompileConditional.analysis.allProjects.toList match
|
||||
{
|
||||
case Nil =>
|
||||
case Nil =>
|
||||
log.debug("No " + tpe + "s detected using default project.")
|
||||
Right(None)
|
||||
case singleDefinition :: Nil => Right(Some(singleDefinition))
|
||||
|
|
@ -114,7 +116,7 @@ private final class BuilderProject(val info: ProjectInfo, val pluginPath: Path,
|
|||
private lazy val pluginProject =
|
||||
{
|
||||
if(pluginPath.exists)
|
||||
Some(new PluginBuilderProject(ProjectInfo(pluginPath.asFile, Nil, None)(rawLogger)))
|
||||
Some(new PluginBuilderProject(ProjectInfo(pluginPath.asFile, Nil, None)(rawLogger, info.app, info.buildScalaVersion)))
|
||||
else
|
||||
None
|
||||
}
|
||||
|
|
@ -137,13 +139,13 @@ private final class BuilderProject(val info: ProjectInfo, val pluginPath: Path,
|
|||
pluginUptodate() = flag
|
||||
saveEnvironment()
|
||||
}
|
||||
|
||||
|
||||
private def pluginTask(f: => Option[String]) = task { if(!pluginUptodate.value) f else None }
|
||||
|
||||
|
||||
lazy val syncPlugins = pluginTask(sync()) dependsOn(extractSources)
|
||||
lazy val extractSources = pluginTask(extract()) dependsOn(update)
|
||||
lazy val update = pluginTask(loadAndUpdate()) dependsOn(compile)
|
||||
|
||||
|
||||
private def sync() = pluginCompileConditional.run orElse { setUptodate(true); None }
|
||||
private def extract() =
|
||||
{
|
||||
|
|
@ -161,7 +163,7 @@ private final class BuilderProject(val info: ProjectInfo, val pluginPath: Path,
|
|||
Control.thread(projectDefinition) {
|
||||
case Some(definition) =>
|
||||
logInfo("\nUpdating plugins...")
|
||||
val pluginInfo = ProjectInfo(info.projectPath.asFile, Nil, None)(rawLogger)
|
||||
val pluginInfo = ProjectInfo(info.projectPath.asFile, Nil, None)(rawLogger, info.app, info.buildScalaVersion)
|
||||
val pluginBuilder = Project.constructProject(pluginInfo, Project.getProjectClass[PluginDefinition](definition, projectClasspath, getClass.getClassLoader))
|
||||
pluginBuilder.projectName() = "Plugin builder"
|
||||
pluginBuilder.projectVersion() = OpaqueVersion("1.0")
|
||||
|
|
@ -184,8 +186,8 @@ private final class BuilderProject(val info: ProjectInfo, val pluginPath: Path,
|
|||
}
|
||||
def plugins = descendents(managedDependencyPath, jarFilter)
|
||||
def pluginClasspath: PathFinder = plugins +++ pluginCompileConfiguration.outputDirectory
|
||||
|
||||
lazy val pluginCompileConditional = new BuilderCompileConditional(pluginCompileConfiguration, "plugin")
|
||||
|
||||
lazy val pluginCompileConditional = new BuilderCompileConditional(pluginCompileConfiguration, buildCompiler, "plugin")
|
||||
lazy val pluginCompileConfiguration =
|
||||
new BuilderCompileConfiguration
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,102 +4,57 @@
|
|||
package sbt
|
||||
|
||||
import java.io.File
|
||||
import xsbt.{AnalyzingCompiler, CompileFailed, CompilerArguments, ComponentManager, ScalaInstance}
|
||||
|
||||
object CompileOrder extends Enumeration
|
||||
{
|
||||
val Mixed, JavaThenScala, ScalaThenJava = Value
|
||||
}
|
||||
private object CompilerCore
|
||||
{
|
||||
def scalaClasspathForJava = FileUtilities.scalaJars.map(_.getAbsolutePath).mkString(File.pathSeparator)
|
||||
}
|
||||
|
||||
sealed abstract class CompilerCore
|
||||
{
|
||||
val ClasspathOptionString = "-classpath"
|
||||
val OutputOptionString = "-d"
|
||||
final def apply(label: String, sources: Iterable[Path], classpath: Iterable[Path], outputDirectory: Path, scalaOptions: Seq[String], log: Logger): Option[String] =
|
||||
apply(label, sources, classpath, outputDirectory, scalaOptions, Nil, CompileOrder.Mixed, log)
|
||||
final def apply(label: String, sources: Iterable[Path], classpath: Iterable[Path], outputDirectory: Path, scalaOptions: Seq[String], javaOptions: Seq[String], order: CompileOrder.Value, log: Logger): Option[String] =
|
||||
{
|
||||
def filteredSources(extension: String) = sources.filter(_.asFile.getName.endsWith(extension))
|
||||
def fileSet(sources: Iterable[Path]) = Set() ++ sources.map(_.asFile)
|
||||
def process(label: String, sources: Iterable[_], act: => Unit) =
|
||||
() => if(sources.isEmpty) log.debug("No " + label + " sources.") else act
|
||||
|
||||
// Returns false if there were errors, true if there were not.
|
||||
protected def process(args: List[String], log: Logger): Boolean
|
||||
// Returns false if there were errors, true if there were not.
|
||||
protected def processJava(args: List[String], log: Logger): Boolean = true
|
||||
protected def scalaClasspathForJava: String
|
||||
def actionStartMessage(label: String): String
|
||||
def actionNothingToDoMessage: String
|
||||
def actionSuccessfulMessage: String
|
||||
def actionUnsuccessfulMessage: String
|
||||
|
||||
private def classpathString(rawClasspathString: String, includeScala: Boolean) =
|
||||
if(includeScala)
|
||||
List(rawClasspathString, scalaClasspathForJava).mkString(File.pathSeparator)
|
||||
else
|
||||
rawClasspathString
|
||||
|
||||
final def apply(label: String, sources: Iterable[Path], classpathString: String, outputDirectory: Path, options: Seq[String], log: Logger): Option[String] =
|
||||
apply(label, sources, classpathString, outputDirectory, options, Nil, CompileOrder.Mixed, log)
|
||||
final def apply(label: String, sources: Iterable[Path], rawClasspathString: String, outputDirectory: Path, options: Seq[String], javaOptions: Seq[String], order: CompileOrder.Value, log: Logger): Option[String] =
|
||||
val javaSources = fileSet(filteredSources(".java"))
|
||||
val scalaSources = fileSet( if(order == CompileOrder.Mixed) sources else filteredSources(".scala") )
|
||||
val classpathSet = fileSet(classpath)
|
||||
val scalaCompile = process("Scala", scalaSources, processScala(scalaSources, classpathSet, outputDirectory.asFile, scalaOptions, log) )
|
||||
val javaCompile = process("Java", javaSources, processJava(javaSources, classpathSet, outputDirectory.asFile, javaOptions, log))
|
||||
try { doCompile(label, sources, outputDirectory, order, log)(javaCompile, scalaCompile) }
|
||||
catch { case e: xsbti.CompileFailed => log.trace(e); Some(e.toString) }
|
||||
}
|
||||
protected def doCompile(label: String, sources: Iterable[Path], outputDirectory: Path, order: CompileOrder.Value, log: Logger)(javaCompile: () => Unit, scalaCompile: () => Unit) =
|
||||
{
|
||||
log.info(actionStartMessage(label))
|
||||
def classpathOption(includeScala: Boolean): List[String] =
|
||||
if(sources.isEmpty)
|
||||
{
|
||||
val classpath = classpathString(rawClasspathString, includeScala)
|
||||
if(classpath.isEmpty)
|
||||
Nil
|
||||
else
|
||||
List(ClasspathOptionString, classpath)
|
||||
log.info(actionNothingToDoMessage)
|
||||
None
|
||||
}
|
||||
val outputDir = outputDirectory.asFile
|
||||
FileUtilities.createDirectory(outputDir, log) orElse
|
||||
else
|
||||
{
|
||||
def classpathAndOut(javac: Boolean): List[String] = OutputOptionString :: outputDir.getAbsolutePath :: classpathOption(javac)
|
||||
|
||||
Control.trapUnit("Compiler error: ", log)
|
||||
FileUtilities.createDirectory(outputDirectory.asFile, log) orElse Control.trapUnit("Compiler error: ", log)
|
||||
{
|
||||
val sourceList = sources.map(_.asFile.getAbsolutePath).toList
|
||||
if(sourceList.isEmpty)
|
||||
{
|
||||
log.info(actionNothingToDoMessage)
|
||||
None
|
||||
}
|
||||
else
|
||||
{
|
||||
def filteredSources(extension: String) = sourceList.filter(_.endsWith(extension))
|
||||
def compile(label: String, sources: List[String], options: Seq[String], includeScala: Boolean)(process: (List[String], Logger) => Boolean) =
|
||||
{
|
||||
if(sources.isEmpty)
|
||||
{
|
||||
log.debug("No "+label+" sources to compile.")
|
||||
true
|
||||
}
|
||||
else
|
||||
{
|
||||
val arguments = (options ++ classpathAndOut(includeScala) ++ sources).toList
|
||||
log.debug(label + " arguments: " + arguments.mkString(" "))
|
||||
process(arguments, log)
|
||||
}
|
||||
}
|
||||
def scalaCompile = () =>
|
||||
{
|
||||
val scalaSourceList = if(order == CompileOrder.Mixed) sourceList else filteredSources(".scala")
|
||||
compile("Scala", scalaSourceList, options, false)(process)
|
||||
}
|
||||
def javaCompile = () =>
|
||||
{
|
||||
val javaSourceList = filteredSources(".java")
|
||||
compile("Java", javaSourceList, javaOptions, true)(processJava)
|
||||
}
|
||||
|
||||
val (first, second) = if(order == CompileOrder.JavaThenScala) (javaCompile, scalaCompile) else (scalaCompile, javaCompile)
|
||||
if(first() && second())
|
||||
{
|
||||
log.info(actionSuccessfulMessage)
|
||||
None
|
||||
}
|
||||
else
|
||||
Some(actionUnsuccessfulMessage)
|
||||
}
|
||||
val (first, second) = if(order == CompileOrder.JavaThenScala) (javaCompile, scalaCompile) else (scalaCompile, javaCompile)
|
||||
first()
|
||||
second()
|
||||
log.info(actionSuccessfulMessage)
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
def actionStartMessage(label: String): String
|
||||
def actionNothingToDoMessage: String
|
||||
def actionSuccessfulMessage: String
|
||||
protected def processScala(sources: Set[File], classpath: Set[File], outputDirectory: File, options: Seq[String], log: Logger): Unit
|
||||
protected def processJava(sources: Set[File], classpath: Set[File], outputDirectory: File, options: Seq[String], log: Logger): Unit
|
||||
}
|
||||
|
||||
sealed abstract class CompilerBase extends CompilerCore
|
||||
|
|
@ -107,199 +62,75 @@ sealed abstract class CompilerBase extends CompilerCore
|
|||
def actionStartMessage(label: String) = "Compiling " + label + " sources..."
|
||||
val actionNothingToDoMessage = "Nothing to compile."
|
||||
val actionSuccessfulMessage = "Compilation successful."
|
||||
val actionUnsuccessfulMessage = "Compilation unsuccessful."
|
||||
}
|
||||
final class ForkCompile(config: ForkScalaCompiler) extends CompilerBase
|
||||
{
|
||||
import java.io.File
|
||||
protected def process(arguments: List[String], log: Logger) =
|
||||
Fork.scalac(config.javaHome, config.compileJVMOptions, config.scalaJars, arguments, log) == 0
|
||||
override protected def processJava(args: List[String], log: Logger) =
|
||||
Fork.javac(config.javaHome, args, log) == 0
|
||||
override protected def scalaClasspathForJava = config.scalaJars.mkString(File.pathSeparator)
|
||||
}
|
||||
object ForkCompile
|
||||
{
|
||||
def apply(config: ForkScalaCompiler, conditional: CompileConditional) =
|
||||
{
|
||||
import conditional.config.{compileOrder, classpath, javaOptions, label, log, options, outputDirectory, sources}
|
||||
// recompile only if any sources were modified after any classes or no classes exist
|
||||
val sourcePaths = sources.get
|
||||
val newestSource = (0L /: sourcePaths)(_ max _.lastModified)
|
||||
val products = (outputDirectory ** GlobFilter("*.class")).get
|
||||
val oldestClass = (java.lang.Long.MAX_VALUE /: products)(_ min _.lastModified)
|
||||
if(products.isEmpty || newestSource > oldestClass)
|
||||
{
|
||||
// full recompile, since we are not doing proper dependency tracking
|
||||
FileUtilities.clean(outputDirectory :: Nil, log)
|
||||
val compiler = new ForkCompile(config)
|
||||
FileUtilities.createDirectory(outputDirectory.asFile, log)
|
||||
compiler(label, sourcePaths, Path.makeString(classpath.get), outputDirectory, options, javaOptions, compileOrder, log)
|
||||
}
|
||||
else
|
||||
{
|
||||
log.info("Compilation up to date.")
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The following code is based on scala.tools.nsc.Main and scala.tools.nsc.ScalaDoc
|
||||
// Copyright 2005-2008 LAMP/EPFL
|
||||
// Original author: Martin Odersky
|
||||
|
||||
final class Compile(maximumErrors: Int) extends CompilerBase
|
||||
final class Compile(maximumErrors: Int, compiler: AnalyzingCompiler, analysisCallback: AnalysisCallback, baseDirectory: Path) extends CompilerBase
|
||||
{
|
||||
protected def process(arguments: List[String], log: Logger) =
|
||||
protected def processScala(sources: Set[File], classpath: Set[File], outputDirectory: File, options: Seq[String], log: Logger)
|
||||
{
|
||||
import scala.tools.nsc.{CompilerCommand, FatalError, Global, Settings, reporters, util}
|
||||
import util.FakePos
|
||||
var reporter = new LoggerReporter(maximumErrors, log)
|
||||
val settings = new Settings(reporter.error)
|
||||
val command = new CompilerCommand(arguments, settings, error, false)
|
||||
|
||||
object compiler extends Global(command.settings, reporter)
|
||||
if(!reporter.hasErrors)
|
||||
{
|
||||
val run = new compiler.Run
|
||||
run compile command.files
|
||||
reporter.printSummary()
|
||||
}
|
||||
!reporter.hasErrors
|
||||
val callbackInterface = new AnalysisInterface(analysisCallback, baseDirectory, outputDirectory)
|
||||
compiler(Set() ++ sources, Set() ++ classpath, outputDirectory, options, true, callbackInterface, maximumErrors, log)
|
||||
}
|
||||
protected def processJava(sources: Set[File], classpath: Set[File], outputDirectory: File, options: Seq[String], log: Logger)
|
||||
{
|
||||
val arguments = (new CompilerArguments(compiler.scalaInstance))(sources, classpath, outputDirectory, options, true)
|
||||
val code = Process("javac", arguments) ! log
|
||||
if( code != 0 ) throw new CompileFailed(arguments.toArray, "javac returned nonzero exit code")
|
||||
}
|
||||
override protected def processJava(args: List[String], log: Logger) =
|
||||
(Process("javac", args) ! log) == 0
|
||||
protected def scalaClasspathForJava = CompilerCore.scalaClasspathForJava
|
||||
}
|
||||
final class Scaladoc(maximumErrors: Int) extends CompilerCore
|
||||
final class Scaladoc(maximumErrors: Int, compiler: AnalyzingCompiler) extends CompilerCore
|
||||
{
|
||||
protected def scalaClasspathForJava = CompilerCore.scalaClasspathForJava
|
||||
protected def process(arguments: List[String], log: Logger) =
|
||||
{
|
||||
import scala.tools.nsc.{doc, CompilerCommand, FatalError, Global, reporters, util}
|
||||
import util.FakePos
|
||||
val reporter = new LoggerReporter(maximumErrors, log)
|
||||
val docSettings: doc.Settings = new doc.Settings(reporter.error)
|
||||
val command = new CompilerCommand(arguments, docSettings, error, false)
|
||||
object compiler extends Global(command.settings, reporter)
|
||||
{
|
||||
override val onlyPresentation = true
|
||||
}
|
||||
if(!reporter.hasErrors)
|
||||
{
|
||||
val run = new compiler.Run
|
||||
run compile command.files
|
||||
val generator = new doc.DefaultDocDriver
|
||||
{
|
||||
lazy val global: compiler.type = compiler
|
||||
lazy val settings = docSettings
|
||||
}
|
||||
generator.process(run.units)
|
||||
reporter.printSummary()
|
||||
}
|
||||
!reporter.hasErrors
|
||||
}
|
||||
protected def processScala(sources: Set[File], classpath: Set[File], outputDirectory: File, options: Seq[String], log: Logger): Unit =
|
||||
compiler.doc(sources, classpath, outputDirectory, options, maximumErrors, log)
|
||||
protected def processJava(sources: Set[File], classpath: Set[File], outputDirectory: File, options: Seq[String], log: Logger) = ()
|
||||
|
||||
def actionStartMessage(label: String) = "Generating API documentation for " + label + " sources..."
|
||||
val actionNothingToDoMessage = "No sources specified."
|
||||
val actionSuccessfulMessage = "API documentation generation successful."
|
||||
def actionUnsuccessfulMessage = "API documentation generation unsuccessful."
|
||||
}
|
||||
|
||||
// The following code is based on scala.tools.nsc.reporters.{AbstractReporter, ConsoleReporter}
|
||||
// Copyright 2002-2009 LAMP/EPFL
|
||||
// Original author: Martin Odersky
|
||||
final class LoggerReporter(maximumErrors: Int, log: Logger) extends scala.tools.nsc.reporters.Reporter
|
||||
final class Console(compiler: AnalyzingCompiler) extends NotNull
|
||||
{
|
||||
import scala.tools.nsc.util.{FakePos,NoPosition,Position}
|
||||
private val positions = new scala.collection.mutable.HashMap[Position, Severity]
|
||||
|
||||
def error(msg: String) { error(FakePos("scalac"), msg) }
|
||||
|
||||
def printSummary()
|
||||
/** Starts an interactive scala interpreter session with the given classpath.*/
|
||||
def apply(classpath: Iterable[Path], log: Logger): Option[String] =
|
||||
apply(classpath, "", log)
|
||||
def apply(classpath: Iterable[Path], initialCommands: String, log: Logger): Option[String] =
|
||||
{
|
||||
if(WARNING.count > 0)
|
||||
log.warn(countElementsAsString(WARNING.count, "warning") + " found")
|
||||
if(ERROR.count > 0)
|
||||
log.error(countElementsAsString(ERROR.count, "error") + " found")
|
||||
def console0 = compiler.console(Set() ++ classpath.map(_.asFile), initialCommands, log)
|
||||
JLine.withJLine( Run.executeTrapExit(console0, log) )
|
||||
}
|
||||
}
|
||||
|
||||
def display(pos: Position, msg: String, severity: Severity)
|
||||
private final class AnalysisInterface(delegate: AnalysisCallback, basePath: Path, outputDirectory: File) extends xsbti.AnalysisCallback with NotNull
|
||||
{
|
||||
val outputPath = Path.fromFile(outputDirectory)
|
||||
def superclassNames = delegate.superclassNames.toSeq.toArray[String]
|
||||
def superclassNotFound(superclassName: String) = delegate.superclassNotFound(superclassName)
|
||||
def beginSource(source: File) = delegate.beginSource(srcPath(source))
|
||||
def foundSubclass(source: File, subclassName: String, superclassName: String, isModule: Boolean) =
|
||||
delegate.foundSubclass(srcPath(source), subclassName, superclassName, isModule)
|
||||
def sourceDependency(dependsOn: File, source: File) =
|
||||
delegate.sourceDependency(srcPath(dependsOn), srcPath(source))
|
||||
def jarDependency(jar: File, source: File) = delegate.jarDependency(jar, srcPath(source))
|
||||
def generatedClass(source: File, clazz: File) = delegate.generatedClass(srcPath(source), classPath(clazz))
|
||||
def endSource(source: File) = delegate.endSource(srcPath(source))
|
||||
def foundApplication(source: File, className: String) = delegate.foundApplication(srcPath(source), className)
|
||||
def classDependency(clazz: File, source: File) =
|
||||
{
|
||||
severity.count += 1
|
||||
if(severity != ERROR || maximumErrors < 0 || severity.count <= maximumErrors)
|
||||
print(severityToLevel(severity), pos, msg)
|
||||
}
|
||||
private def severityToLevel(severity: Severity): Level.Value =
|
||||
severity match
|
||||
val sourcePath = srcPath(source)
|
||||
Path.relativize(outputPath, clazz) match
|
||||
{
|
||||
case ERROR => Level.Error
|
||||
case WARNING => Level.Warn
|
||||
case INFO => Level.Info
|
||||
}
|
||||
|
||||
private def print(level: Level.Value, posIn: Position, msg: String)
|
||||
{
|
||||
// the implicits keep source compatibility with the changes in 2.8 : Position.{source,line,column} are no longer Options
|
||||
implicit def anyToOption[T <: AnyRef](t: T): Option[T] = Some(t)
|
||||
implicit def intToOption(t: Int): Option[Int] = Some(t)
|
||||
val pos =
|
||||
posIn match
|
||||
{
|
||||
case null | NoPosition => NoPosition
|
||||
case x: FakePos => x
|
||||
case x =>
|
||||
posIn.inUltimateSource(posIn.source.get)
|
||||
}
|
||||
pos match
|
||||
{
|
||||
case NoPosition => log.log(level, msg)
|
||||
case FakePos(fmsg) => log.log(level, fmsg+" "+msg)
|
||||
case _ =>
|
||||
val sourcePrefix = pos.source.map(_.file.path).getOrElse("")
|
||||
val lineNumberString = pos.line.map(line => ":" + line + ":").getOrElse(":") + " "
|
||||
log.log(level, sourcePrefix + lineNumberString + msg)
|
||||
if (!pos.line.isEmpty)
|
||||
{
|
||||
val lineContent = pos.lineContent.stripLineEnd
|
||||
log.log(level, lineContent) // source line with error/warning
|
||||
for(offset <- pos.offset; src <- pos.source)
|
||||
{
|
||||
val pointer = offset - src.lineToOffset(src.offsetToLine(offset))
|
||||
val pointerSpace = (lineContent: Seq[Char]).take(pointer).map { case '\t' => '\t'; case _ => ' ' }
|
||||
log.log(level, pointerSpace.mkString + "^") // pointer to the column position of the error/warning
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
override def reset =
|
||||
{
|
||||
super.reset
|
||||
positions.clear
|
||||
}
|
||||
|
||||
protected def info0(pos: Position, msg: String, severity: Severity, force: Boolean)
|
||||
{
|
||||
severity match
|
||||
{
|
||||
case WARNING | ERROR =>
|
||||
{
|
||||
if(!testAndLog(pos, severity))
|
||||
display(pos, msg, severity)
|
||||
}
|
||||
case _ => display(pos, msg, severity)
|
||||
}
|
||||
}
|
||||
|
||||
private def testAndLog(pos: Position, severity: Severity): Boolean =
|
||||
{
|
||||
if(pos == null || pos.offset.isEmpty)
|
||||
false
|
||||
else if(positions.get(pos).map(_ >= severity).getOrElse(false))
|
||||
true
|
||||
else
|
||||
{
|
||||
positions(pos) = severity
|
||||
false
|
||||
case None => // dependency is a class file outside of the output directory
|
||||
delegate.classDependency(clazz, sourcePath)
|
||||
case Some(relativeToOutput) => // dependency is a product of a source not included in this compilation
|
||||
delegate.productDependency(relativeToOutput, sourcePath)
|
||||
}
|
||||
}
|
||||
def relativizeOrAbs(base: Path, file: File) = Path.relativize(base, file).getOrElse(Path.fromFile(file))
|
||||
def classPath(file: File) = relativizeOrAbs(outputPath, file)
|
||||
def srcPath(file: File) = relativizeOrAbs(basePath, file)
|
||||
}
|
||||
|
|
@ -3,29 +3,31 @@
|
|||
*/
|
||||
package sbt
|
||||
|
||||
import xsbt.AnalyzingCompiler
|
||||
|
||||
trait Conditional[Source, Product, External] extends NotNull
|
||||
{
|
||||
type AnalysisType <: TaskAnalysis[Source, Product, External]
|
||||
val analysis: AnalysisType = loadAnalysis
|
||||
|
||||
|
||||
protected def loadAnalysis: AnalysisType
|
||||
protected def log: Logger
|
||||
|
||||
protected def productType: String
|
||||
protected def productTypePlural: String
|
||||
|
||||
|
||||
protected def sourcesToProcess: Iterable[Source]
|
||||
|
||||
|
||||
protected def sourceExists(source: Source): Boolean
|
||||
protected def sourceLastModified(source: Source): Long
|
||||
|
||||
|
||||
protected def productExists(product: Product): Boolean
|
||||
protected def productLastModified(product: Product): Long
|
||||
|
||||
|
||||
protected def externalInfo(externals: Iterable[External]): Iterable[(External, ExternalInfo)]
|
||||
|
||||
|
||||
protected def execute(cAnalysis: ConditionalAnalysis): Option[String]
|
||||
|
||||
|
||||
final case class ExternalInfo(available: Boolean, lastModified: Long) extends NotNull
|
||||
trait ConditionalAnalysis extends NotNull
|
||||
{
|
||||
|
|
@ -35,7 +37,7 @@ trait Conditional[Source, Product, External] extends NotNull
|
|||
def invalidatedSourcesCount: Int
|
||||
def removedSourcesCount: Int
|
||||
}
|
||||
|
||||
|
||||
final def run =
|
||||
{
|
||||
val result = execute(analyze)
|
||||
|
|
@ -45,7 +47,7 @@ trait Conditional[Source, Product, External] extends NotNull
|
|||
private def analyze =
|
||||
{
|
||||
import scala.collection.mutable.HashSet
|
||||
|
||||
|
||||
val sourcesSnapshot = sourcesToProcess
|
||||
val removedSources = new HashSet[Source]
|
||||
removedSources ++= analysis.allSources
|
||||
|
|
@ -56,10 +58,10 @@ trait Conditional[Source, Product, External] extends NotNull
|
|||
log.debug("Source " + removed + " removed.")
|
||||
analysis.removeDependent(removed)
|
||||
}
|
||||
|
||||
|
||||
val unmodified = new HashSet[Source]
|
||||
val modified = new HashSet[Source]
|
||||
|
||||
|
||||
for(source <- sourcesSnapshot)
|
||||
{
|
||||
if(isSourceModified(source))
|
||||
|
|
@ -102,7 +104,7 @@ trait Conditional[Source, Product, External] extends NotNull
|
|||
analysis.removeExternalDependency(external)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val handled = new scala.collection.mutable.HashSet[Source]
|
||||
val transitive = !java.lang.Boolean.getBoolean("sbt.intransitive")
|
||||
def markModified(changed: Iterable[Source]) { for(c <- changed if !handled.contains(c)) markSourceModified(c) }
|
||||
|
|
@ -119,10 +121,10 @@ trait Conditional[Source, Product, External] extends NotNull
|
|||
markModified(modified.toList)
|
||||
if(transitive)
|
||||
removedSources.foreach(markDependenciesModified)
|
||||
|
||||
|
||||
for(changed <- removedSources ++ modified)
|
||||
analysis.removeSource(changed)
|
||||
|
||||
|
||||
new ConditionalAnalysis
|
||||
{
|
||||
def dirtySources = wrap.Wrappers.readOnly(modified)
|
||||
|
|
@ -138,7 +140,7 @@ trait Conditional[Source, Product, External] extends NotNull
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected def checkLastModified = true
|
||||
protected def noProductsImpliesModified = true
|
||||
protected def isSourceModified(source: Source) =
|
||||
|
|
@ -155,7 +157,7 @@ trait Conditional[Source, Product, External] extends NotNull
|
|||
val sourceModificationTime = sourceLastModified(source)
|
||||
def isOutofdate(p: Product) =
|
||||
!productExists(p) || (checkLastModified && productLastModified(p) < sourceModificationTime)
|
||||
|
||||
|
||||
sourceProducts.find(isOutofdate) match
|
||||
{
|
||||
case Some(modifiedProduct) =>
|
||||
|
|
@ -206,7 +208,7 @@ abstract class CompileConfiguration extends AbstractCompileConfiguration
|
|||
def testDefinitionClassNames: Iterable[String]
|
||||
}
|
||||
import java.io.File
|
||||
class CompileConditional(override val config: CompileConfiguration) extends AbstractCompileConditional(config)
|
||||
class CompileConditional(override val config: CompileConfiguration, compiler: AnalyzingCompiler) extends AbstractCompileConditional(config, compiler)
|
||||
{
|
||||
import config._
|
||||
type AnalysisType = CompileAnalysis
|
||||
|
|
@ -220,7 +222,7 @@ class CompileConditional(override val config: CompileConfiguration) extends Abst
|
|||
analysis.addTest(sourcePath, TestDefinition(isModule, subclassName, superclassName))
|
||||
}
|
||||
}
|
||||
abstract class AbstractCompileConditional(val config: AbstractCompileConfiguration) extends Conditional[Path, Path, File]
|
||||
abstract class AbstractCompileConditional(val config: AbstractCompileConfiguration, val compiler: AnalyzingCompiler) extends Conditional[Path, Path, File]
|
||||
{
|
||||
import config._
|
||||
type AnalysisType <: BasicCompileAnalysis
|
||||
|
|
@ -232,31 +234,32 @@ abstract class AbstractCompileConditional(val config: AbstractCompileConfigurati
|
|||
a
|
||||
}
|
||||
protected def constructAnalysis(analysisPath: Path, projectPath: Path, log: Logger): AnalysisType
|
||||
|
||||
|
||||
protected def log = config.log
|
||||
|
||||
|
||||
protected def productType = "class"
|
||||
protected def productTypePlural = "classes"
|
||||
protected def sourcesToProcess = sources.get
|
||||
|
||||
|
||||
protected def sourceExists(source: Path) = source.asFile.exists
|
||||
protected def sourceLastModified(source: Path) = source.asFile.lastModified
|
||||
|
||||
|
||||
protected def productExists(product: Path) = product.asFile.exists
|
||||
protected def productLastModified(product: Path) = product.asFile.lastModified
|
||||
|
||||
|
||||
private def libraryJar = compiler.scalaInstance.libraryJar
|
||||
protected def externalInfo(externals: Iterable[File]) =
|
||||
{
|
||||
val (classpathJars, classpathDirs) = ClasspathUtilities.buildSearchPaths(classpath.get)
|
||||
for(external <- externals) yield
|
||||
{
|
||||
val available = external.exists && ClasspathUtilities.onClasspath(classpathJars, classpathDirs, external)
|
||||
val available = external.exists && (external == libraryJar || ClasspathUtilities.onClasspath(classpathJars, classpathDirs, external) )
|
||||
if(!available)
|
||||
log.debug("External " + external + (if(external.exists) " not on classpath." else " does not exist."))
|
||||
(external, ExternalInfo(available, external.lastModified))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
import ChangeDetection.{LastModifiedOnly, HashOnly, HashAndLastModified, HashAndProductsExist}
|
||||
protected def changeDetectionMethod: ChangeDetection.Value = HashAndProductsExist
|
||||
override protected def checkLastModified = changeDetectionMethod != HashAndProductsExist
|
||||
|
|
@ -271,7 +274,7 @@ abstract class AbstractCompileConditional(val config: AbstractCompileConfigurati
|
|||
case HashOnly => hashModified(source)
|
||||
case LastModifiedOnly => super.isSourceModified(source)
|
||||
}
|
||||
|
||||
|
||||
import scala.collection.mutable.{Buffer, ListBuffer}
|
||||
private val newHashes: Buffer[(Path, Option[Array[Byte]])] = new ListBuffer
|
||||
private def warnHashError(source: Path, message: String)
|
||||
|
|
@ -312,22 +315,21 @@ abstract class AbstractCompileConditional(val config: AbstractCompileConfigurati
|
|||
log.info(executeAnalysis.toString)
|
||||
finishHashes()
|
||||
import executeAnalysis.dirtySources
|
||||
|
||||
|
||||
// the output directory won't show up in the classpath unless it exists, so do this before classpath.get
|
||||
val outputDir = outputDirectory.asFile
|
||||
FileUtilities.createDirectory(outputDir, log)
|
||||
|
||||
|
||||
val cp = classpath.get
|
||||
if(!dirtySources.isEmpty)
|
||||
checkClasspath(cp)
|
||||
val classpathString = Path.makeString(cp)
|
||||
val id = AnalysisCallback.register(analysisCallback)
|
||||
val allOptions = (("-Xplugin:" + FileUtilities.sbtJar.getAbsolutePath) ::
|
||||
("-P:sbt-analyzer:callback:" + id.toString) :: Nil) ++ options
|
||||
def run = (new Compile(config.maxErrors))(label, dirtySources, classpathString, outputDirectory, allOptions, javaOptions, compileOrder, log)
|
||||
def run =
|
||||
{
|
||||
val compile = new Compile(config.maxErrors, compiler, analysisCallback, projectPath)
|
||||
compile(label, dirtySources, cp, outputDirectory, options, javaOptions, compileOrder, log)
|
||||
}
|
||||
val loader = ClasspathUtilities.toLoader(cp)
|
||||
val r = classfile.Analyze(projectPath, outputDirectory, dirtySources, sourceRoots.get, log)(analysis.allProducts, analysisCallback, loader)(run)
|
||||
AnalysisCallback.unregister(id)
|
||||
if(log.atLevel(Level.Debug))
|
||||
{
|
||||
/** This checks that the plugin accounted for all classes in the output directory.*/
|
||||
|
|
@ -377,7 +379,7 @@ abstract class AbstractCompileConditional(val config: AbstractCompileConfigurati
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected def analysisCallback: AnalysisCallback
|
||||
}
|
||||
object ChangeDetection extends Enumeration
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@ abstract class BasicScalaProject extends ScalaProject with BasicDependencyProjec
|
|||
def manifestClassPath: Option[String] = None
|
||||
def dependencies = info.dependencies ++ subProjects.values.toList
|
||||
|
||||
val mainCompileConditional = new CompileConditional(mainCompileConfiguration)
|
||||
val testCompileConditional = new CompileConditional(testCompileConfiguration)
|
||||
val mainCompileConditional = new CompileConditional(mainCompileConfiguration, buildCompiler)
|
||||
val testCompileConditional = new CompileConditional(testCompileConfiguration, buildCompiler)
|
||||
|
||||
def compileOrder = CompileOrder.Mixed
|
||||
|
||||
|
|
@ -95,6 +95,8 @@ abstract class BasicScalaProject extends ScalaProject with BasicDependencyProjec
|
|||
TestFilter(new impl.TestQuickFilter(analysis, failedOnly, path, log)) :: TestListeners(new impl.TestStatusReporter(path, log) :: Nil) :: Nil
|
||||
}
|
||||
|
||||
def consoleInit = ""
|
||||
|
||||
protected def includeTest(test: String): Boolean = true
|
||||
|
||||
/** This is called to create the initial directories when a user makes a new project from
|
||||
|
|
@ -212,31 +214,26 @@ abstract class BasicScalaProject extends ScalaProject with BasicDependencyProjec
|
|||
|
||||
/** Configures forking the compiler and runner. Use ForkScalaCompiler, ForkScalaRun or mix together.*/
|
||||
def fork: Option[ForkScala] = None
|
||||
private def doCompile(conditional: CompileConditional) =
|
||||
{
|
||||
fork match
|
||||
{
|
||||
case Some(fc: ForkScalaCompiler) => ForkCompile(fc, conditional)
|
||||
case _ => conditional.run
|
||||
}
|
||||
}
|
||||
private def getRunner =
|
||||
private def doCompile(conditional: CompileConditional) = conditional.run
|
||||
implicit def defaultRunner: ScalaRun =
|
||||
{
|
||||
fork match
|
||||
{
|
||||
case Some(fr: ForkScalaRun) => new ForkRun(fr)
|
||||
case _ => Run
|
||||
case _ => new Run(buildCompiler)
|
||||
}
|
||||
}
|
||||
|
||||
def basicConsoleTask = consoleTask(consoleClasspath, consoleInit)
|
||||
|
||||
protected def runTask(mainClass: String): MethodTask = task { args => runTask(Some(mainClass), runClasspath, args) dependsOn(compile, copyResources) }
|
||||
|
||||
protected def compileAction = task { doCompile(mainCompileConditional) } describedAs MainCompileDescription
|
||||
protected def testCompileAction = task { doCompile(testCompileConditional) } dependsOn compile describedAs TestCompileDescription
|
||||
protected def cleanAction = cleanTask(outputPath, cleanOptions) describedAs CleanDescription
|
||||
protected def runAction = task { args => runTask(getMainClass(true), runClasspath, args, getRunner) dependsOn(compile, copyResources) } describedAs RunDescription
|
||||
protected def consoleQuickAction = consoleTask(consoleClasspath, Run) describedAs ConsoleQuickDescription
|
||||
protected def consoleAction = consoleTask(consoleClasspath, Run).dependsOn(testCompile, copyResources, copyTestResources) describedAs ConsoleDescription
|
||||
protected def runAction = task { args => runTask(getMainClass(true), runClasspath, args) dependsOn(compile, copyResources) } describedAs RunDescription
|
||||
protected def consoleQuickAction = basicConsoleTask describedAs ConsoleQuickDescription
|
||||
protected def consoleAction = basicConsoleTask.dependsOn(testCompile, copyResources, copyTestResources) describedAs ConsoleDescription
|
||||
protected def docAction = scaladocTask(mainLabel, mainSources, mainDocPath, docClasspath, documentOptions).dependsOn(compile) describedAs DocDescription
|
||||
protected def docTestAction = scaladocTask(testLabel, testSources, testDocPath, docClasspath, documentOptions).dependsOn(testCompile) describedAs TestDocDescription
|
||||
protected def testAction = defaultTestTask(testOptions)
|
||||
|
|
@ -315,6 +312,7 @@ abstract class BasicScalaProject extends ScalaProject with BasicDependencyProjec
|
|||
mapScalaModule(snapshot.scalaCompiler, ScalaArtifacts.CompilerID)
|
||||
}
|
||||
override def watchPaths = mainSources +++ testSources +++ mainResources +++ testResources
|
||||
private def mapScalaModule(in: Iterable[_], id: String) = in.map(jar => ModuleID(ScalaArtifacts.Organization, id, buildScalaVersion))
|
||||
}
|
||||
abstract class BasicWebScalaProject extends BasicScalaProject with WebScalaProject with WebScalaPaths
|
||||
{ p =>
|
||||
|
|
@ -327,7 +325,7 @@ abstract class BasicWebScalaProject extends BasicScalaProject with WebScalaProje
|
|||
|
||||
lazy val jettyInstance = new JettyRunner(jettyConfiguration)
|
||||
|
||||
def jettyConfiguration =
|
||||
def jettyConfiguration: JettyConfiguration =
|
||||
new DefaultJettyConfiguration
|
||||
{
|
||||
def classpath = jettyRunClasspath
|
||||
|
|
@ -335,6 +333,7 @@ abstract class BasicWebScalaProject extends BasicScalaProject with WebScalaProje
|
|||
def war = jettyWebappPath
|
||||
def contextPath = jettyContextPath
|
||||
def classpathName = "test"
|
||||
def parentLoader = buildScalaInstance.loader
|
||||
def scanDirectories = p.scanDirectories.map(_.asFile)
|
||||
def scanInterval = p.scanInterval
|
||||
def port = jettyPort
|
||||
|
|
@ -443,12 +442,6 @@ object BasicScalaProject
|
|||
log.warn("Multiple classes with a main method were detected. Specify main class explicitly with:")
|
||||
log.warn(" override mainClass = Some(\"className\")")
|
||||
}
|
||||
private def mapScalaModule(in: Iterable[_], id: String) =
|
||||
{
|
||||
ScalaVersion.current.toList.flatMap { scalaVersion =>
|
||||
in.map(jar => ModuleID(ScalaArtifacts.Organization, id, scalaVersion))
|
||||
}
|
||||
}
|
||||
}
|
||||
object BasicWebScalaProject
|
||||
{
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ trait Environment
|
|||
def get: Option[T] = resolve.toOption
|
||||
/** Returns full information about this property's current value. */
|
||||
def resolve: PropertyResolution[T]
|
||||
|
||||
|
||||
def foreach(f: T => Unit): Unit = resolve.foreach(f)
|
||||
}
|
||||
|
||||
|
||||
/** Creates a system property with the given name and no default value.*/
|
||||
def system[T](propName: String)(implicit format: Format[T]): Property[T]
|
||||
/** Creates a system property with the given name and the given default value to use if no value is explicitly specified.*/
|
||||
|
|
@ -67,12 +67,12 @@ trait BasicEnvironment extends Environment
|
|||
protected def parentEnvironment: Option[BasicEnvironment] = None
|
||||
/** The identifier used in messages to refer to this environment. */
|
||||
def environmentLabel = envBackingPath.absolutePath
|
||||
|
||||
|
||||
private[this] var isModified = false
|
||||
private[sbt] def setEnvironmentModified(modified: Boolean) { synchronized { isModified = modified } }
|
||||
private[this] def isEnvironmentModified = synchronized { isModified }
|
||||
|
||||
|
||||
|
||||
|
||||
implicit val IntFormat: Format[Int] = new SimpleFormat[Int] { def fromString(s: String) = java.lang.Integer.parseInt(s) }
|
||||
implicit val LongFormat: Format[Long] = new SimpleFormat[Long] { def fromString(s: String) = java.lang.Long.parseLong(s) }
|
||||
implicit val DoubleFormat: Format[Double] = new SimpleFormat[Double] { def fromString(s: String) = java.lang.Double.parseDouble(s) }
|
||||
|
|
@ -94,8 +94,8 @@ trait BasicEnvironment extends Environment
|
|||
def fromString(s: String) = Version.fromString(s).fold(msg => error(msg), x => x)
|
||||
}
|
||||
implicit val FileFormat = Format.file
|
||||
|
||||
|
||||
|
||||
|
||||
/** Implementation of 'Property' for user-defined properties. */
|
||||
private[sbt] class UserProperty[T](lazyDefaultValue: => Option[T], format: Format[T], inheritEnabled: Boolean,
|
||||
inheritFirst: Boolean, private[BasicEnvironment] val manifest: Manifest[T]) extends Property[T]
|
||||
|
|
@ -126,22 +126,26 @@ trait BasicEnvironment extends Environment
|
|||
case None =>
|
||||
val inherited = inheritedValue
|
||||
// note that the following means the default value will not be used if an exception occurs inheriting
|
||||
inherited orElse
|
||||
{
|
||||
defaultValue match
|
||||
{
|
||||
case Some(v) => DefinedValue(v, false, true)
|
||||
case None => inherited
|
||||
}
|
||||
}
|
||||
inherited orElse getDefault(inherited)
|
||||
}
|
||||
private def resolveDefaultFirst =
|
||||
(explicitValue() orElse defaultValue) match
|
||||
explicitValue() match
|
||||
{
|
||||
case Some(v) => DefinedValue(v, false, explicitValue().isEmpty)
|
||||
case None => inheritedValue
|
||||
case Some(v) => DefinedValue(v, false, false)
|
||||
case None => getDefault(inheritedValue)
|
||||
}
|
||||
|
||||
private def getDefault(orElse: => PropertyResolution[T]): PropertyResolution[T] =
|
||||
try
|
||||
{
|
||||
defaultValue match
|
||||
{
|
||||
case Some(v) => DefinedValue(v, false, true)
|
||||
case None => orElse
|
||||
}
|
||||
} catch { case e: Exception =>
|
||||
ResolutionException("Error while evaluating default value for property", Some(e))
|
||||
}
|
||||
|
||||
private def inheritedValue: PropertyResolution[T] =
|
||||
{
|
||||
val propOption = if(inheritEnabled) parentProperty else None
|
||||
|
|
@ -152,7 +156,7 @@ trait BasicEnvironment extends Environment
|
|||
}
|
||||
}
|
||||
private def parentProperty = for(parent <- parentEnvironment; n <- name; prop <- parent.propertyMap.get(n)) yield prop
|
||||
|
||||
|
||||
private def tryToInherit[R](prop: BasicEnvironment#UserProperty[R]): PropertyResolution[T] =
|
||||
{
|
||||
if(prop.manifest <:< manifest)
|
||||
|
|
@ -167,9 +171,9 @@ trait BasicEnvironment extends Environment
|
|||
case DefinedValue(v, isInherited, isDefault) => DefinedValue(v, true, isDefault)
|
||||
case x => x
|
||||
}
|
||||
|
||||
|
||||
override def toString = nameString + "=" + resolve
|
||||
|
||||
|
||||
/** Gets the explicitly set value converted to a 'String'.*/
|
||||
private[sbt] def getStringValue: Option[String] = explicitValue().map(format.toString)
|
||||
/** Explicitly sets the value for this property by converting the given string value.*/
|
||||
|
|
@ -217,12 +221,12 @@ trait BasicEnvironment extends Environment
|
|||
}
|
||||
override def toString = name + "=" + resolve
|
||||
}
|
||||
|
||||
|
||||
def system[T](propertyName: String)(implicit format: Format[T]): Property[T] =
|
||||
new SystemProperty[T](propertyName, None, format)
|
||||
def systemOptional[T](propertyName: String, defaultValue: => T)(implicit format: Format[T]): Property[T] =
|
||||
new SystemProperty[T](propertyName, Some(defaultValue), format)
|
||||
|
||||
|
||||
def property[T](implicit manifest: Manifest[T], format: Format[T]): Property[T] =
|
||||
new UserProperty[T](None, format, true, false, manifest)
|
||||
def propertyLocal[T](implicit manifest: Manifest[T], format: Format[T]): Property[T] =
|
||||
|
|
@ -231,7 +235,7 @@ trait BasicEnvironment extends Environment
|
|||
propertyOptional(defaultValue, false)(manifest, format)
|
||||
def propertyOptional[T](defaultValue: => T, inheritFirst: Boolean)(implicit manifest: Manifest[T], format: Format[T]): Property[T] =
|
||||
new UserProperty[T](Some(defaultValue), format, true, inheritFirst, manifest)
|
||||
|
||||
|
||||
private type AnyUserProperty = UserProperty[_]
|
||||
/** Maps property name to property. The map is constructed by reflecting vals defined on this object,
|
||||
* so it should not be referenced during initialization or else subclass properties will be missed.**/
|
||||
|
|
@ -254,7 +258,7 @@ trait BasicEnvironment extends Environment
|
|||
log.error("Error loading properties from " + environmentLabel + " : " + errorMsg)
|
||||
map //.readOnly (not currently in 2.8)
|
||||
}
|
||||
|
||||
|
||||
def propertyNames: Iterable[String] = propertyMap.keys.toList
|
||||
def getPropertyNamed(name: String): Option[UserProperty[_]] = propertyMap.get(name)
|
||||
def propertyNamed(name: String): UserProperty[_] = propertyMap(name)
|
||||
|
|
|
|||
|
|
@ -28,30 +28,30 @@ trait BasicIntegrationTesting extends ScalaIntegrationTesting with IntegrationTe
|
|||
self: BasicScalaProject =>
|
||||
|
||||
import BasicScalaIntegrationTesting._
|
||||
|
||||
|
||||
lazy val integrationTestCompile = integrationTestCompileAction
|
||||
lazy val integrationTest = integrationTestAction
|
||||
|
||||
val integrationTestCompileConditional = new CompileConditional(integrationTestCompileConfiguration)
|
||||
val integrationTestCompileConditional = new CompileConditional(integrationTestCompileConfiguration, buildCompiler)
|
||||
|
||||
protected def integrationTestAction = integrationTestTask(integrationTestFrameworks, integrationTestClasspath, integrationTestCompileConditional.analysis, integrationTestOptions) dependsOn integrationTestCompile describedAs IntegrationTestCompileDescription
|
||||
protected def integrationTestCompileAction = integrationTestCompileTask() dependsOn compile describedAs IntegrationTestDescription
|
||||
|
||||
protected def integrationTestCompileTask() = task{ integrationTestCompileConditional.run }
|
||||
|
||||
def integrationTestOptions: Seq[TestOption] =
|
||||
def integrationTestOptions: Seq[TestOption] =
|
||||
TestSetup(() => pretests) ::
|
||||
TestCleanup(() => posttests) ::
|
||||
testOptions.toList
|
||||
def integrationTestCompileOptions = testCompileOptions
|
||||
def javaIntegrationTestCompileOptions: Seq[JavaCompileOption] = testJavaCompileOptions
|
||||
|
||||
|
||||
def integrationTestConfiguration = if(useIntegrationTestConfiguration) Configurations.IntegrationTest else Configurations.Test
|
||||
def integrationTestClasspath = fullClasspath(integrationTestConfiguration) +++ optionalClasspath
|
||||
|
||||
|
||||
def integrationTestLabel = "integration-test"
|
||||
def integrationTestCompileConfiguration = new IntegrationTestCompileConfig
|
||||
|
||||
|
||||
protected def integrationTestDependencies = new LibraryDependencies(this, integrationTestCompileConditional)
|
||||
|
||||
def integrationTestFrameworks = testFrameworks
|
||||
|
|
|
|||
|
|
@ -18,15 +18,16 @@ object ControlEvent extends Enumeration
|
|||
val Start, Header, Finish = Value
|
||||
}
|
||||
|
||||
abstract class Logger extends NotNull
|
||||
abstract class Logger extends xsbt.CompileLogger with xsbt.IvyLogger
|
||||
{
|
||||
def getLevel: Level.Value
|
||||
def setLevel(newLevel: Level.Value)
|
||||
def enableTrace(flag: Boolean)
|
||||
def traceEnabled: Boolean
|
||||
|
||||
|
||||
def atLevel(level: Level.Value) = level.id >= getLevel.id
|
||||
def trace(t: => Throwable): Unit
|
||||
final def verbose(message: => String): Unit = debug(message)
|
||||
final def debug(message: => String): Unit = log(Level.Debug, message)
|
||||
final def info(message: => String): Unit = log(Level.Info, message)
|
||||
final def warn(message: => String): Unit = log(Level.Warn, message)
|
||||
|
|
@ -34,7 +35,7 @@ abstract class Logger extends NotNull
|
|||
def success(message: => String): Unit
|
||||
def log(level: Level.Value, message: => String): Unit
|
||||
def control(event: ControlEvent.Value, message: => String): Unit
|
||||
|
||||
|
||||
def logAll(events: Seq[LogEvent]): Unit
|
||||
/** Defined in terms of other methods in Logger and should not be called from them. */
|
||||
final def log(event: LogEvent)
|
||||
|
|
@ -49,6 +50,14 @@ abstract class Logger extends NotNull
|
|||
case c: ControlEvent => control(c.event, c.msg)
|
||||
}
|
||||
}
|
||||
|
||||
import xsbti.F0
|
||||
def debug(msg: F0[String]): Unit = log(Level.Debug, msg)
|
||||
def warn(msg: F0[String]): Unit = log(Level.Warn, msg)
|
||||
def info(msg: F0[String]): Unit = log(Level.Info, msg)
|
||||
def error(msg: F0[String]): Unit = log(Level.Error, msg)
|
||||
def trace(msg: F0[Throwable]) = trace(msg.apply)
|
||||
def log(level: Level.Value, msg: F0[String]): Unit = log(level, msg.apply)
|
||||
}
|
||||
|
||||
/** Implements the level-setting methods of Logger.*/
|
||||
|
|
@ -68,7 +77,7 @@ final class SynchronizedLogger(delegate: Logger) extends Logger
|
|||
def setLevel(newLevel: Level.Value) { synchronized { delegate.setLevel(newLevel) } }
|
||||
def enableTrace(enabled: Boolean) { synchronized { delegate.enableTrace(enabled) } }
|
||||
def traceEnabled: Boolean = { synchronized { delegate.traceEnabled } }
|
||||
|
||||
|
||||
def trace(t: => Throwable) { synchronized { delegate.trace(t) } }
|
||||
def log(level: Level.Value, message: => String) { synchronized { delegate.log(level, message) } }
|
||||
def success(message: => String) { synchronized { delegate.success(message) } }
|
||||
|
|
@ -139,12 +148,12 @@ final class BufferedLogger(delegate: Logger) extends Logger
|
|||
{
|
||||
private[this] val buffers = wrap.Wrappers.weakMap[Thread, Buffer[LogEvent]]
|
||||
private[this] var recordingAll = false
|
||||
|
||||
|
||||
private[this] def getOrCreateBuffer = buffers.getOrElseUpdate(key, createBuffer)
|
||||
private[this] def buffer = if(recordingAll) Some(getOrCreateBuffer) else buffers.get(key)
|
||||
private[this] def createBuffer = new ListBuffer[LogEvent]
|
||||
private[this] def key = Thread.currentThread
|
||||
|
||||
|
||||
@deprecated def startRecording() = recordAll()
|
||||
/** Enables buffering for logging coming from the current Thread. */
|
||||
def record(): Unit = synchronized { buffers(key) = createBuffer }
|
||||
|
|
@ -162,7 +171,7 @@ final class BufferedLogger(delegate: Logger) extends Logger
|
|||
try { f }
|
||||
finally { Control.trap(stopAll()) }
|
||||
}
|
||||
|
||||
|
||||
/** Flushes the buffer to the delegate logger for the current thread. This method calls logAll on the delegate
|
||||
* so that the messages are written consecutively. The buffer is cleared in the process. */
|
||||
def play(): Unit =
|
||||
|
|
@ -194,7 +203,7 @@ final class BufferedLogger(delegate: Logger) extends Logger
|
|||
playAll()
|
||||
clearAll()
|
||||
}
|
||||
|
||||
|
||||
def setLevel(newLevel: Level.Value): Unit =
|
||||
synchronized
|
||||
{
|
||||
|
|
@ -209,7 +218,7 @@ final class BufferedLogger(delegate: Logger) extends Logger
|
|||
buffer.foreach{_ += new SetTrace(flag) }
|
||||
delegate.enableTrace(flag)
|
||||
}
|
||||
|
||||
|
||||
def trace(t: => Throwable): Unit =
|
||||
doBufferableIf(traceEnabled, new Trace(t), _.trace(t))
|
||||
def success(message: => String): Unit =
|
||||
|
|
@ -246,18 +255,18 @@ final class BufferedLogger(delegate: Logger) extends Logger
|
|||
object ConsoleLogger
|
||||
{
|
||||
private val formatEnabled = ansiSupported && !formatExplicitlyDisabled
|
||||
|
||||
|
||||
private[this] def formatExplicitlyDisabled = java.lang.Boolean.getBoolean("sbt.log.noformat")
|
||||
private[this] def ansiSupported =
|
||||
try { jline.Terminal.getTerminal.isANSISupported }
|
||||
catch { case e: Exception => !isWindows }
|
||||
|
||||
|
||||
private[this] def os = System.getProperty("os.name")
|
||||
private[this] def isWindows = os.toLowerCase.indexOf("windows") >= 0
|
||||
}
|
||||
|
||||
/** A logger that logs to the console. On supported systems, the level labels are
|
||||
* colored.
|
||||
* colored.
|
||||
*
|
||||
* This logger is not thread-safe.*/
|
||||
class ConsoleLogger extends BasicLogger
|
||||
|
|
@ -311,7 +320,7 @@ class ConsoleLogger extends BasicLogger
|
|||
System.out.println()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def logAll(events: Seq[LogEvent]) = System.out.synchronized { events.foreach(log) }
|
||||
def control(event: ControlEvent.Value, message: => String)
|
||||
{ log(labelColor(Level.Info), Level.Info.toString, Console.BLUE, message) }
|
||||
|
|
@ -329,7 +338,7 @@ object Level extends Enumeration with NotNull
|
|||
* uses this label. Because the label for levels is defined in this module, the success
|
||||
* label is also defined here. */
|
||||
val SuccessLabel = "success"
|
||||
|
||||
|
||||
// added because elements was renamed to iterator in 2.8.0 nightly
|
||||
def levels = Debug :: Info :: Warn :: Error :: Nil
|
||||
/** Returns the level with the given name wrapped in Some, or None if no level exists for that name. */
|
||||
|
|
|
|||
|
|
@ -5,47 +5,56 @@ package sbt
|
|||
|
||||
import scala.collection.immutable.TreeSet
|
||||
|
||||
private trait RunCompleteAction extends NotNull
|
||||
private case class Exit(code: Int) extends RunCompleteAction
|
||||
private object Reload extends RunCompleteAction
|
||||
private case class Exit(code: Int) extends xsbti.Exit
|
||||
{
|
||||
require(code >= 0)
|
||||
}
|
||||
private case class Reboot(val scalaVersion: String, argsList: List[String], configuration: xsbti.AppConfiguration) extends xsbti.Reboot
|
||||
{
|
||||
def app = configuration.provider.id
|
||||
def arguments = argsList.toArray
|
||||
def baseDirectory = configuration.baseDirectory
|
||||
}
|
||||
|
||||
/** This class is the entry point for sbt. If it is given any arguments, it interprets them
|
||||
* as actions, executes the corresponding actions, and exits. If there were no arguments provided,
|
||||
* sbt enters interactive mode.*/
|
||||
object Main
|
||||
{
|
||||
/** The entry point for sbt. If arguments are specified, they are interpreted as actions, executed,
|
||||
* and then the program terminates. If no arguments are specified, the program enters interactive
|
||||
* mode. Call run if you need to run sbt in the same JVM.*/
|
||||
def main(args: Array[String])
|
||||
{
|
||||
val exitCode = run(args)
|
||||
if(exitCode == RebootExitCode)
|
||||
{
|
||||
println("Rebooting is not supported when the sbt loader is not used.")
|
||||
println("Please manually restart sbt.")
|
||||
}
|
||||
System.exit(exitCode)
|
||||
}
|
||||
val RebootExitCode = -1
|
||||
val NormalExitCode = 0
|
||||
val SetupErrorExitCode = 1
|
||||
val SetupDeclinedExitCode = 2
|
||||
val LoadErrorExitCode = 3
|
||||
val UsageErrorExitCode = 4
|
||||
val BuildErrorExitCode = 5
|
||||
def run(args: Array[String]): Int =
|
||||
val ProgramErrorExitCode = 6
|
||||
}
|
||||
|
||||
import Main._
|
||||
|
||||
class xMain extends xsbti.AppMain
|
||||
{
|
||||
final def run(configuration: xsbti.AppConfiguration): xsbti.MainResult =
|
||||
{
|
||||
def run0(remainingArguments: List[String], buildScalaVersion: Option[String]): xsbti.MainResult =
|
||||
{
|
||||
try { run(configuration, remainingArguments, buildScalaVersion) }
|
||||
catch { case re: ReloadException => run0(re.remainingArguments, re.buildScalaVersion) }
|
||||
}
|
||||
run0(configuration.arguments.map(_.trim).toList, None)
|
||||
}
|
||||
final def run(configuration: xsbti.AppConfiguration, remainingArguments: List[String], buildScalaVersion: Option[String]): xsbti.MainResult =
|
||||
{
|
||||
val startTime = System.currentTimeMillis
|
||||
Project.loadProject match
|
||||
Project.loadProject(configuration.provider, buildScalaVersion) match
|
||||
{
|
||||
case err: LoadSetupError =>
|
||||
println("\n" + err.message)
|
||||
ExitHooks.runExitHooks(Project.bootLogger)
|
||||
SetupErrorExitCode
|
||||
Exit(SetupErrorExitCode)
|
||||
case LoadSetupDeclined =>
|
||||
ExitHooks.runExitHooks(Project.bootLogger)
|
||||
SetupDeclinedExitCode
|
||||
Exit(SetupDeclinedExitCode)
|
||||
case err: LoadError =>
|
||||
{
|
||||
val log = Project.bootLogger
|
||||
|
|
@ -63,81 +72,138 @@ object Main
|
|||
}
|
||||
line match
|
||||
{
|
||||
case Some(l) => if(!isTerminateAction(l)) run(args) else NormalExitCode
|
||||
case None => LoadErrorExitCode
|
||||
case Some(l) => if(!isTerminateAction(l)) run(configuration, remainingArguments, buildScalaVersion) else Exit(NormalExitCode)
|
||||
case None => Exit(LoadErrorExitCode)
|
||||
}
|
||||
}
|
||||
case success: LoadSuccess =>
|
||||
{
|
||||
import success.project
|
||||
val doNext: RunCompleteAction =
|
||||
// in interactive mode, fill all undefined properties
|
||||
if(args.length > 0 || fillUndefinedProjectProperties(project.projectClosure.toList.reverse))
|
||||
startProject(project, args, startTime)
|
||||
else
|
||||
new Exit(NormalExitCode)
|
||||
ExitHooks.runExitHooks(project.log)
|
||||
doNext match
|
||||
try
|
||||
{
|
||||
case Reload => run(args)
|
||||
case x: Exit => x.code
|
||||
// in interactive mode, fill all undefined properties
|
||||
if(configuration.arguments.length > 0 || fillUndefinedProjectProperties(project.projectClosure.toList.reverse))
|
||||
startProject(project, configuration, remainingArguments, startTime)
|
||||
else
|
||||
Exit(NormalExitCode)
|
||||
}
|
||||
finally { ExitHooks.runExitHooks(project.log) }
|
||||
}
|
||||
}
|
||||
}
|
||||
private def startProject(project: Project, args: Array[String], startTime: Long): RunCompleteAction =
|
||||
{
|
||||
project.log.info("Building project " + project.name + " " + project.version.toString + " using " + project.getClass.getName)
|
||||
val scalaVersionOpt = ScalaVersion.current orElse project.scalaVersion.get
|
||||
for(sbtVersion <- project.sbtVersion.get; scalaVersion <- scalaVersionOpt if !sbtVersion.isEmpty && !scalaVersion.isEmpty)
|
||||
project.log.info(" with sbt " + sbtVersion + " and Scala " + scalaVersion)
|
||||
args match
|
||||
private def initialize(args: List[String]): List[String] =
|
||||
args.lastOption match
|
||||
{
|
||||
case Array() =>
|
||||
CrossBuild.load() match
|
||||
{
|
||||
case None =>
|
||||
project.log.info("No actions specified, interactive session started. Execute 'help' for more information.")
|
||||
val doNext = interactive(project)
|
||||
printTime(project, startTime, "session")
|
||||
doNext
|
||||
case Some(cross) =>
|
||||
crossBuildNext(project, cross)
|
||||
new Exit(RebootExitCode)
|
||||
}
|
||||
case CrossBuild(action) =>
|
||||
val exitCode =
|
||||
CrossBuild.load() match
|
||||
{
|
||||
case None => if(startCrossBuild(project, action)) RebootExitCode else BuildErrorExitCode
|
||||
case Some(cross) => if(crossBuildNext(project, cross)) RebootExitCode else NormalExitCode
|
||||
}
|
||||
new Exit(exitCode)
|
||||
case _ =>
|
||||
val source = args.toList.elements
|
||||
def nextCommand = if(source.hasNext) Right(source.next) else Left(new Exit(NormalExitCode) )
|
||||
val result = loop(project, project, p => nextCommand, false)
|
||||
result match
|
||||
{
|
||||
case Exit(NormalExitCode) => project.log.success("Build completed successfully.")
|
||||
case Exit(_) => project.log.error("Error during build.")
|
||||
case _ => ()
|
||||
}
|
||||
printTime(project, startTime, "build")
|
||||
result
|
||||
case None => InteractiveCommand :: Nil
|
||||
case Some(InteractiveCommand | ExitCommand | QuitCommand) => args
|
||||
case _ => args ::: ExitCommand :: Nil
|
||||
}
|
||||
private def startProject(project: Project, configuration: xsbti.AppConfiguration, remainingArguments: List[String], startTime: Long): xsbti.MainResult =
|
||||
{
|
||||
project.log.info("Building project " + project.name + " " + project.version.toString + " against Scala " + project.buildScalaVersion)
|
||||
project.log.info(" using " + project.getClass.getName + " with sbt " + project.sbtVersion.value + " and Scala " + project.scalaVersion.value)
|
||||
|
||||
processArguments(project, initialize(remainingArguments), configuration, startTime) match
|
||||
{
|
||||
case e: xsbti.Exit =>
|
||||
printTime(project, startTime, "session")
|
||||
if(e.code == NormalExitCode)
|
||||
project.log.success("Build completed successfully.")
|
||||
else
|
||||
project.log.error("Error during build.")
|
||||
e
|
||||
case r => r
|
||||
}
|
||||
}
|
||||
private def crossBuildNext(project: Project, cross: CrossBuild) =
|
||||
private def processArguments(baseProject: Project, arguments: List[String], configuration: xsbti.AppConfiguration, startTime: Long): xsbti.MainResult =
|
||||
{
|
||||
val setScalaVersion = (newVersion: String) => { System.setProperty(ScalaVersion.LiveKey, newVersion); () }
|
||||
val complete =
|
||||
if(handleAction(project, cross.command))
|
||||
cross.versionComplete(setScalaVersion)
|
||||
def process(project: Project, arguments: List[String], isInteractive: Boolean): xsbti.MainResult =
|
||||
{
|
||||
def saveProject(newArgs: List[String]) = if(baseProject.name != project.name) (ProjectAction + " " + project.name) :: newArgs else newArgs
|
||||
arguments match
|
||||
{
|
||||
case "" :: tail => process(project, tail, isInteractive)
|
||||
case (ExitCommand | QuitCommand) :: _ => Exit(NormalExitCode)
|
||||
case RebootCommand :: tail => Reboot(project.scalaVersion.value, saveProject(tail), configuration)
|
||||
case InteractiveCommand :: _ => process(project, prompt(baseProject, project) :: arguments, true)
|
||||
case SpecificBuild(version, action) :: tail =>
|
||||
if(Some(version) != baseProject.info.buildScalaVersion)
|
||||
throw new ReloadException(saveProject(action :: tail), Some(version))
|
||||
else
|
||||
process(project, tail, isInteractive)
|
||||
case CrossBuild(action) :: tail =>
|
||||
if(checkAction(project, action)) process(project, CrossBuild(project, action) ::: tail, isInteractive)
|
||||
else if(isInteractive) process(project, tail, isInteractive)
|
||||
else Exit(UsageErrorExitCode)
|
||||
case SetProject(name) :: tail =>
|
||||
SetProject(baseProject, name, project) match
|
||||
{
|
||||
case Some(newProject) => process(newProject, tail, isInteractive)
|
||||
case None => process(project, if(isInteractive) tail else ExitCommand :: tail, isInteractive)
|
||||
}
|
||||
case action :: tail =>
|
||||
val success = processAction(baseProject, project, action, isInteractive)
|
||||
if(success || isInteractive) process(project, tail, isInteractive) else Exit(BuildErrorExitCode)
|
||||
case Nil => project.log.error("Invalid internal sbt state: no arguments"); Exit(ProgramErrorExitCode)
|
||||
}
|
||||
}
|
||||
process(baseProject, arguments, false)
|
||||
}
|
||||
object SetProject
|
||||
{
|
||||
def unapply(s: String) =
|
||||
if(s.startsWith(ProjectAction + " "))
|
||||
Some(s.substring(ProjectAction.length + 1))
|
||||
else
|
||||
cross.error(setScalaVersion)
|
||||
if(complete)
|
||||
printTime(project, cross.startTime, "cross-build")
|
||||
!complete
|
||||
None
|
||||
def apply(baseProject: Project, projectName: String, currentProject: Project) =
|
||||
{
|
||||
val found = baseProject.projectClosure.find(_.name == projectName)
|
||||
found match
|
||||
{
|
||||
case Some(newProject) => printProject("Set current project to ", newProject)
|
||||
case None => currentProject.log.error("Invalid project name '" + projectName + "' (type 'projects' to list available projects).")
|
||||
}
|
||||
found
|
||||
}
|
||||
}
|
||||
object SpecificBuild
|
||||
{
|
||||
val pattern = """\+\+(\S+)\s*(.*)""".r.pattern
|
||||
def unapply(s: String) =
|
||||
{
|
||||
val m = pattern.matcher(s)
|
||||
if(m.matches)
|
||||
Some(m.group(1).trim, m.group(2).trim)
|
||||
else
|
||||
None
|
||||
}
|
||||
}
|
||||
object CrossBuild
|
||||
{
|
||||
def unapply(s: String) = if(s.startsWith("+") && !s.startsWith("++")) Some(s.substring(1)) else None
|
||||
def apply(project: Project, action: String) =
|
||||
{
|
||||
val againstScalaVersions = project.crossScalaVersions
|
||||
if(againstScalaVersions.isEmpty)
|
||||
{
|
||||
Console.println("Project does not declare any Scala versions to cross-build against, building against current version...")
|
||||
action :: Nil
|
||||
}
|
||||
else
|
||||
againstScalaVersions.toList.map("++" + _ + " " + action)
|
||||
}
|
||||
}
|
||||
// todo: project.log.info("No actions specified, interactive session started. Execute 'help' for more information.")
|
||||
private def prompt(baseProject: Project, project: Project): String =
|
||||
{
|
||||
val projectNames = baseProject.projectClosure.map(_.name)
|
||||
val prefixes = ContinuousExecutePrefix :: CrossBuildPrefix :: Nil
|
||||
val completors = new Completors(ProjectAction, projectNames, interactiveCommands, List(GetAction, SetAction), prefixes)
|
||||
val reader = new JLineReader(baseProject.historyPath, completors, baseProject.log)
|
||||
val methodCompletions = for( (name, method) <- project.methods) yield (name, method.completions)
|
||||
reader.setVariableCompletions(project.taskNames, project.propertyNames, methodCompletions)
|
||||
reader.readLine("> ").getOrElse(ExitCommand)
|
||||
}
|
||||
|
||||
/** The name of the command that loads a console with access to the current project through the variable 'project'.*/
|
||||
|
|
@ -150,18 +216,19 @@ object Main
|
|||
val ProjectAction = "project"
|
||||
/** The name of the command that shows all available projects.*/
|
||||
val ShowProjectsAction = "projects"
|
||||
val ExitCommand = "exit"
|
||||
val QuitCommand = "quit"
|
||||
val InteractiveCommand = "shell"
|
||||
/** The list of lowercase command names that may be used to terminate the program.*/
|
||||
val TerminateActions: Iterable[String] = "exit" :: "quit" :: Nil
|
||||
val TerminateActions: Iterable[String] = ExitCommand :: QuitCommand :: Nil
|
||||
/** The name of the command that sets the value of the property given as its argument.*/
|
||||
val SetAction = "set"
|
||||
/** The name of the command that gets the value of the property given as its argument.*/
|
||||
val GetAction = "get"
|
||||
/** The name of the command that displays the help message. */
|
||||
val HelpAction = "help"
|
||||
/** The command for rebooting sbt. Requires sbt to have been launched by the loader.*/
|
||||
val RebootCommand = "reboot"
|
||||
/** The name of the command that reloads a project. This is useful for when the project definition has changed. */
|
||||
val ReloadAction = "reload"
|
||||
/** The command for reloading sbt.*/
|
||||
val RebootCommand = "reload"
|
||||
/** The name of the command that toggles logging stacktraces. */
|
||||
val TraceCommand = "trace"
|
||||
/** The name of the command that compiles all sources continuously when they are modified. */
|
||||
|
|
@ -170,8 +237,6 @@ object Main
|
|||
val ContinuousExecutePrefix = "~"
|
||||
/** The prefix used to identify a request to execute the remaining input across multiple Scala versions.*/
|
||||
val CrossBuildPrefix = "+"
|
||||
/** Error message for when the user tries to prefix an action with CrossBuildPrefix but the loader is not used.*/
|
||||
val CrossBuildUnsupported = "Cross-building is not supported when the loader is not used."
|
||||
|
||||
/** The number of seconds between polling by the continuous compile command.*/
|
||||
val ContinuousCompilePollDelaySeconds = 1
|
||||
|
|
@ -183,96 +248,24 @@ object Main
|
|||
private def logLevels: Iterable[String] = TreeSet.empty[String] ++ Level.levels.map(_.toString)
|
||||
/** The list of all interactive commands other than logging level.*/
|
||||
private def basicCommands: Iterable[String] = TreeSet(ShowProjectsAction, ShowActions, ShowCurrent, HelpAction,
|
||||
RebootCommand, ReloadAction, TraceCommand, ContinuousCompileCommand, ProjectConsoleAction)
|
||||
RebootCommand, TraceCommand, ContinuousCompileCommand, ProjectConsoleAction)
|
||||
|
||||
/** Enters interactive mode for the given root project. It uses JLine for tab completion and
|
||||
* history. It returns normally when the user terminates or reloads the interactive session. That is,
|
||||
* it does not call System.exit to quit.
|
||||
**/
|
||||
private def interactive(baseProject: Project): RunCompleteAction =
|
||||
{
|
||||
val projectNames = baseProject.projectClosure.map(_.name)
|
||||
val prefixes = ContinuousExecutePrefix :: CrossBuildPrefix :: Nil
|
||||
val completors = new Completors(ProjectAction, projectNames, interactiveCommands, List(GetAction, SetAction), prefixes)
|
||||
val reader = new JLineReader(baseProject.historyPath, completors, baseProject.log)
|
||||
def updateTaskCompletions(project: Project)
|
||||
private def processAction(baseProject: Project, currentProject: Project, action: String, isInteractive: Boolean): Boolean =
|
||||
action match
|
||||
{
|
||||
val methodCompletions = for( (name, method) <- project.methods) yield (name, method.completions)
|
||||
reader.setVariableCompletions(project.taskNames, project.propertyNames, methodCompletions)
|
||||
case HelpAction => displayHelp(isInteractive); true
|
||||
case ShowProjectsAction => baseProject.projectClosure.foreach(listProject); true
|
||||
case ProjectConsoleAction =>
|
||||
showResult(Run.projectConsole(currentProject), currentProject.log)
|
||||
case _ =>
|
||||
if(action.startsWith(SetAction + " "))
|
||||
setProperty(currentProject, action.substring(SetAction.length + 1))
|
||||
else if(action.startsWith(GetAction + " "))
|
||||
getProperty(currentProject, action.substring(GetAction.length + 1))
|
||||
else
|
||||
handleCommand(currentProject, action)
|
||||
}
|
||||
|
||||
def readCommand(currentProject: Project) =
|
||||
{
|
||||
updateTaskCompletions(currentProject) // this is done before every command because the completions could change due to the action previously invoked
|
||||
reader.readLine("> ").toRight(new Exit(NormalExitCode))
|
||||
}
|
||||
|
||||
loop(baseProject, baseProject, readCommand, true)
|
||||
}
|
||||
|
||||
/** Prompts the user for the next command using 'currentProject' as context.
|
||||
* If the command indicates that the user wishes to terminate or reload the session,
|
||||
* the function returns the appropriate value.
|
||||
* Otherwise, the command is handled and this function is called again
|
||||
* (tail recursively) to prompt for the next command. */
|
||||
private def loop(baseProject: Project, currentProject: Project, nextCommand: Project => Either[RunCompleteAction, String], isInteractive: Boolean): RunCompleteAction =
|
||||
nextCommand(currentProject).right.flatMap{ line => process(baseProject, currentProject, line, isInteractive) } match
|
||||
{
|
||||
case Left(complete) => complete
|
||||
case Right(project) => loop(baseProject, project, nextCommand, isInteractive)
|
||||
}
|
||||
private def process(baseProject: Project, currentProject: Project, line: String, isInteractive: Boolean): Either[RunCompleteAction, Project] =
|
||||
{
|
||||
def keepCurrent(success: Boolean) = if(success) Right(currentProject) else Left(new Exit(BuildErrorExitCode) )
|
||||
def interactiveKeepCurrent(success: Boolean) = keepCurrent(success || isInteractive)
|
||||
def keep(u: Unit) = Right(currentProject)
|
||||
|
||||
val trimmed = line.trim
|
||||
if(trimmed.isEmpty)
|
||||
Right(currentProject)
|
||||
else if(isTerminateAction(trimmed))
|
||||
Left(new Exit(NormalExitCode))
|
||||
else if(ReloadAction == trimmed)
|
||||
Left(Reload)
|
||||
else if(RebootCommand == trimmed)
|
||||
{
|
||||
if(!isInteractive) currentProject.log.warn("'reboot' does not pick up changes to 'scala.version' in batch mode.")
|
||||
System.setProperty(ScalaVersion.LiveKey, "")
|
||||
Left(new Exit(RebootExitCode))
|
||||
}
|
||||
else if(trimmed.startsWith(CrossBuildPrefix))
|
||||
{
|
||||
if(startCrossBuild(currentProject, trimmed.substring(CrossBuildPrefix.length).trim))
|
||||
Left(new Exit(RebootExitCode))
|
||||
else
|
||||
Right(currentProject)
|
||||
}
|
||||
else if(trimmed.startsWith(ProjectAction + " "))
|
||||
{
|
||||
val projectName = trimmed.substring(ProjectAction.length + 1)
|
||||
baseProject.projectClosure.find(_.name == projectName) match
|
||||
{
|
||||
case Some(newProject) =>
|
||||
printProject("Set current project to ", newProject)
|
||||
Right(newProject)
|
||||
case None =>
|
||||
currentProject.log.error("Invalid project name '" + projectName + "' (type 'projects' to list available projects).")
|
||||
keepCurrent(isInteractive)
|
||||
}
|
||||
}
|
||||
else if(trimmed == HelpAction)
|
||||
keep(displayHelp(isInteractive))
|
||||
else if(trimmed == ShowProjectsAction)
|
||||
keep(baseProject.projectClosure.foreach(listProject))
|
||||
else if(trimmed.startsWith(SetAction + " "))
|
||||
interactiveKeepCurrent( setProperty(currentProject, trimmed.substring(SetAction.length + 1)) )
|
||||
else if(trimmed.startsWith(GetAction + " "))
|
||||
interactiveKeepCurrent( getProperty(currentProject, trimmed.substring(GetAction.length + 1)) )
|
||||
else if(trimmed == ProjectConsoleAction)
|
||||
interactiveKeepCurrent(showResult(Run.projectConsole(currentProject), currentProject.log))
|
||||
else
|
||||
interactiveKeepCurrent( handleCommand(currentProject, trimmed) )
|
||||
}
|
||||
private def printCmd(name:String, desc:String) = Console.println("\t" + name + ": " + desc)
|
||||
val BatchHelpHeader = "You may execute any project action or method or one of the commands described below."
|
||||
val InteractiveHelpHeader = "You may execute any project action or one of the commands described below. Only one action " +
|
||||
|
|
@ -288,9 +281,8 @@ object Main
|
|||
printCmd(ContinuousExecutePrefix + " <command>", "Executes the project specified action or method whenever source files change.")
|
||||
printCmd(CrossBuildPrefix + " <command>", "Executes the project specified action or method for all versions of Scala defined in crossScalaVersions.")
|
||||
printCmd(ShowActions, "Shows all available actions.")
|
||||
printCmd(RebootCommand, "Changes to scala.version or sbt.version are processed and the project definition is reloaded.")
|
||||
printCmd(RebootCommand, "Reloads sbt, picking up modifications to sbt.version or scala.version and recompiling modified project definitions.")
|
||||
printCmd(HelpAction, "Displays this help message.")
|
||||
printCmd(ReloadAction, "Reloads sbt, recompiling modified project definitions if necessary.")
|
||||
printCmd(ShowCurrent, "Shows the current project and logging level of that project.")
|
||||
printCmd(Level.levels.mkString(", "), "Set logging for the current project to the specified level.")
|
||||
printCmd(TraceCommand, "Toggles whether logging stack traces is enabled.")
|
||||
|
|
@ -300,31 +292,13 @@ object Main
|
|||
printCmd(SetAction + " <property> <value>", "Sets the value of the property given as its argument.")
|
||||
printCmd(GetAction + " <property>", "Gets the value of the property given as its argument.")
|
||||
printCmd(ProjectConsoleAction, "Enters the Scala interpreter with the current project bound to the variable 'current' and all members imported.")
|
||||
if(!isInteractive)
|
||||
printCmd(InteractiveCommand, "Enters the sbt interactive shell")
|
||||
}
|
||||
private def listProject(p: Project) = printProject("\t", p)
|
||||
private def printProject(prefix: String, p: Project): Unit =
|
||||
Console.println(prefix + p.name + " " + p.version)
|
||||
|
||||
private def startCrossBuild(project: Project, action: String) =
|
||||
{
|
||||
checkBooted && checkAction(project, action) &&
|
||||
{
|
||||
val againstScalaVersions = project.crossScalaVersions
|
||||
val versionsDefined = !againstScalaVersions.isEmpty
|
||||
if(versionsDefined)
|
||||
CrossBuild(againstScalaVersions, action, System.currentTimeMillis)
|
||||
else
|
||||
Console.println("Project does not declare any Scala versions to cross-build against.")
|
||||
versionsDefined
|
||||
}
|
||||
}
|
||||
private def checkBooted =
|
||||
Project.booted ||
|
||||
{
|
||||
Console.println(CrossBuildUnsupported)
|
||||
false
|
||||
}
|
||||
|
||||
/** Handles the given command string provided at the command line. Returns false if there was an error*/
|
||||
private def handleCommand(project: Project, command: String): Boolean =
|
||||
{
|
||||
|
|
@ -530,7 +504,7 @@ object Main
|
|||
val v = m.group(2)
|
||||
if(v == null) "" else v.trim
|
||||
}
|
||||
def notePending(changed: String): Unit = Console.println(" Build will use " + changed + newValue + " after running 'reboot' command or restarting sbt.")
|
||||
def notePending(changed: String): Unit = Console.println(" Build will use " + changed + newValue + " after running 'reload' command or restarting sbt.")
|
||||
project.getPropertyNamed(name) match
|
||||
{
|
||||
case Some(property) =>
|
||||
|
|
@ -586,77 +560,9 @@ object Main
|
|||
private def getArgumentError(log: Logger) = logError(log)("Invalid arguments for 'get': expected property name.")
|
||||
private def setProjectError(log: Logger) = logError(log)("Invalid arguments for 'project': expected project name.")
|
||||
private def logError(log: Logger)(s: String) = { log.error(s); false }
|
||||
}
|
||||
private class CrossBuild(val remainingScalaVersions: Set[String], val command: String, val startTime: Long)
|
||||
{
|
||||
def error(setScalaVersion: String => Unit) = clearScalaVersion(setScalaVersion)
|
||||
private def clearScalaVersion(setScalaVersion: String => Unit) =
|
||||
|
||||
private final class ReloadException(val remainingArguments: List[String], val buildScalaVersion: Option[String]) extends RuntimeException
|
||||
{
|
||||
CrossBuild.clear()
|
||||
setScalaVersion("")
|
||||
true
|
||||
override def fillInStackTrace = this
|
||||
}
|
||||
def versionComplete(setScalaVersion: String => Unit) =
|
||||
{
|
||||
val remaining = remainingScalaVersions - ScalaVersion.currentString
|
||||
if(remaining.isEmpty)
|
||||
clearScalaVersion(setScalaVersion)
|
||||
else
|
||||
{
|
||||
CrossBuild.setProperties(remaining, command, startTime.toString)
|
||||
setScalaVersion(remaining.toSeq.first)
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
private object CrossBuild
|
||||
{
|
||||
private val RemainingScalaVersionsKey = "sbt.remaining.scala.versions"
|
||||
private val CrossCommandKey = "sbt.cross.build.command"
|
||||
private val StartTimeKey = "sbt.cross.start.time"
|
||||
private def setProperties(remainingScalaVersions: Set[String], command: String, startTime: String)
|
||||
{
|
||||
System.setProperty(RemainingScalaVersionsKey, remainingScalaVersions.mkString(" "))
|
||||
System.setProperty(CrossCommandKey, command)
|
||||
System.setProperty(StartTimeKey, startTime)
|
||||
}
|
||||
private def getProperty(key: String) =
|
||||
{
|
||||
val value = System.getProperty(key)
|
||||
if(value == null)
|
||||
""
|
||||
else
|
||||
value.trim
|
||||
}
|
||||
private def clear() { setProperties(Set.empty, "", "") }
|
||||
def load() =
|
||||
{
|
||||
val command = getProperty(CrossCommandKey)
|
||||
val remaining = getProperty(RemainingScalaVersionsKey)
|
||||
val startTime = getProperty(StartTimeKey)
|
||||
if(command.isEmpty || remaining.isEmpty || startTime.isEmpty)
|
||||
None
|
||||
else
|
||||
Some(new CrossBuild(Set(remaining.split(" ") : _*), command, startTime.toLong))
|
||||
}
|
||||
def apply(remainingScalaVersions: Set[String], command: String, startTime: Long) =
|
||||
{
|
||||
setProperties(remainingScalaVersions, command, startTime.toString)
|
||||
new CrossBuild(remainingScalaVersions, command, startTime)
|
||||
}
|
||||
import Main.CrossBuildPrefix
|
||||
def unapply(s: String): Option[String] =
|
||||
{
|
||||
val trimmed = s.trim
|
||||
if(trimmed.startsWith(CrossBuildPrefix))
|
||||
Some(trimmed.substring(CrossBuildPrefix.length).trim)
|
||||
else
|
||||
None
|
||||
}
|
||||
def unapply(s: Array[String]): Option[String] =
|
||||
s match
|
||||
{
|
||||
case Array(CrossBuild(crossBuildAction)) => Some(crossBuildAction)
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,8 @@
|
|||
*/
|
||||
package sbt
|
||||
|
||||
import xsbti.{AppProvider, ScalaProvider}
|
||||
import xsbt.{AnalyzingCompiler, ComponentManager, ScalaInstance}
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
import scala.collection._
|
||||
|
|
@ -20,9 +22,9 @@ trait Project extends TaskManager with Dag[Project] with BasicEnvironment
|
|||
lg
|
||||
}
|
||||
protected def defaultLoggingLevel = Level.Info
|
||||
|
||||
|
||||
trait ActionOption extends NotNull
|
||||
|
||||
|
||||
/** Basic project information. */
|
||||
def info: ProjectInfo
|
||||
/** The project name. */
|
||||
|
|
@ -33,7 +35,7 @@ trait Project extends TaskManager with Dag[Project] with BasicEnvironment
|
|||
final def organization: String = projectOrganization.value
|
||||
/** True if the project should cater to a quick throwaway project setup.*/
|
||||
def scratch = projectScratch.value
|
||||
|
||||
|
||||
final type ManagerType = Project
|
||||
final type ManagedTask = Project#Task
|
||||
/** The tasks declared on this project. */
|
||||
|
|
@ -51,7 +53,7 @@ trait Project extends TaskManager with Dag[Project] with BasicEnvironment
|
|||
/** A description of all available tasks in this project and all dependencies. If there
|
||||
* are different tasks with the same name, only one will be included. */
|
||||
def taskList: String = descriptionList(deepTasks)
|
||||
|
||||
|
||||
final def taskName(task: Task) = tasks.find( _._2 eq task ).map(_._1)
|
||||
/** A description of all available tasks in this project and all dependencies and all
|
||||
* available method tasks in this project, but not of dependencies. If there
|
||||
|
|
@ -83,7 +85,7 @@ trait Project extends TaskManager with Dag[Project] with BasicEnvironment
|
|||
* main use within sbt is in ParentProject.*/
|
||||
def subProjects: Map[String, Project] = immutable.Map.empty
|
||||
def projectClosure: List[Project] = Dag.topologicalSort(this)(p => p.dependencies ++ p.subProjects.values.toList)
|
||||
|
||||
|
||||
def call(name: String, parameters: Array[String]): Option[String] =
|
||||
{
|
||||
methods.get(name) match
|
||||
|
|
@ -98,7 +100,7 @@ trait Project extends TaskManager with Dag[Project] with BasicEnvironment
|
|||
case Nil => None
|
||||
case x => Some(Set(x: _*).mkString("\n"))
|
||||
}
|
||||
|
||||
|
||||
/** Executes the task with the given name. This involves executing the task for all
|
||||
* project dependencies (transitive) and then for this project. Not every dependency
|
||||
* must define a task with the given name. If this project and all dependencies
|
||||
|
|
@ -128,7 +130,7 @@ trait Project extends TaskManager with Dag[Project] with BasicEnvironment
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Logs the list of projects at the debug level.*/
|
||||
private def showBuildOrder(order: Iterable[Project])
|
||||
{
|
||||
|
|
@ -136,22 +138,22 @@ trait Project extends TaskManager with Dag[Project] with BasicEnvironment
|
|||
order.foreach(x => log.debug(" " + x.name) )
|
||||
log.debug("")
|
||||
}
|
||||
|
||||
|
||||
/** Converts a String to a path relative to the project directory of this project. */
|
||||
implicit def path(component: String): Path = info.projectPath / component
|
||||
/** Converts a String to a simple name filter. * has the special meaning: zero or more of any character */
|
||||
implicit def filter(simplePattern: String): NameFilter = GlobFilter(simplePattern)
|
||||
|
||||
|
||||
/** Loads the project at the given path and declares the project to have the given
|
||||
* dependencies. This method will configure the project according to the
|
||||
* project/ directory in the directory denoted by path.*/
|
||||
def project(path: Path, deps: Project*): Project = getProject(Project.loadProject(path, deps, Some(this), log), path)
|
||||
|
||||
def project(path: Path, deps: Project*): Project = getProject(Project.loadProject(path, deps, Some(this), log, info.app, info.buildScalaVersion), path)
|
||||
|
||||
/** Loads the project at the given path using the given name and inheriting this project's version.
|
||||
* The builder class is the default builder class, sbt.DefaultProject. The loaded project is declared
|
||||
* to have the given dependencies. Any project/build/ directory for the project is ignored.*/
|
||||
def project(path: Path, name: String, deps: Project*): Project = project(path, name, Project.DefaultBuilderClass, deps: _*)
|
||||
|
||||
|
||||
/** Loads the project at the given path using the given name and inheriting it's version from this project.
|
||||
* The Project implementation used is given by builderClass. The dependencies are declared to be
|
||||
* deps. Any project/build/ directory for the project is ignored.*/
|
||||
|
|
@ -164,8 +166,8 @@ trait Project extends TaskManager with Dag[Project] with BasicEnvironment
|
|||
* The construct function is used to obtain the Project instance. Any project/build/ directory for the project
|
||||
* is ignored. The project is declared to have the dependencies given by deps.*/
|
||||
def project[P <: Project](path: Path, name: String, construct: ProjectInfo => P, deps: Project*): P =
|
||||
initialize(construct(ProjectInfo(path.asFile, deps, Some(this))(log)), Some(new SetupInfo(name, None, None, false)), log)
|
||||
|
||||
initialize(construct(ProjectInfo(path.asFile, deps, Some(this))(log, info.app, info.buildScalaVersion)), Some(new SetupInfo(name, None, None, false)), log)
|
||||
|
||||
/** Initializes the project directories when a user has requested that sbt create a new project.*/
|
||||
def initializeDirectories() {}
|
||||
/** True if projects should be run in parallel, false if they should run sequentially.
|
||||
|
|
@ -177,11 +179,11 @@ trait Project extends TaskManager with Dag[Project] with BasicEnvironment
|
|||
case Some(parent) => parent.parallelExecution
|
||||
case None => false
|
||||
}
|
||||
|
||||
|
||||
/** True if a project and its dependencies should be checked to ensure that their
|
||||
* output directories are not the same, false if they should not be checked. */
|
||||
def shouldCheckOutputDirectories = true
|
||||
|
||||
|
||||
/** The list of directories to which this project writes. This is used to verify that multiple
|
||||
* projects have not been defined with the same output directories. */
|
||||
def outputDirectories: Iterable[Path] = outputPath :: Nil
|
||||
|
|
@ -193,7 +195,7 @@ trait Project extends TaskManager with Dag[Project] with BasicEnvironment
|
|||
def outputPath = crossPath(outputRootPath)
|
||||
def outputRootPath: Path = outputDirectoryName
|
||||
def outputDirectoryName = DefaultOutputDirectoryName
|
||||
|
||||
|
||||
private def getProject(result: LoadResult, path: Path): Project =
|
||||
result match
|
||||
{
|
||||
|
|
@ -202,31 +204,50 @@ trait Project extends TaskManager with Dag[Project] with BasicEnvironment
|
|||
case err: LoadError => Predef.error("Error loading project at path " + path + " : " + err.message)
|
||||
case success: LoadSuccess => success.project
|
||||
}
|
||||
|
||||
|
||||
/** The property for the project's version. */
|
||||
final val projectVersion = property[Version]
|
||||
/** The property for the project's name. */
|
||||
final val projectName = propertyLocalF[String](NonEmptyStringFormat)
|
||||
/** The property for the project's organization. Defaults to the parent project's organization or the project name if there is no parent. */
|
||||
final val projectOrganization = propertyOptional[String](normalizedName, true)
|
||||
/** The property that defines the version of Scala to build this project with by default. This property is only
|
||||
* ready by `sbt` on startup and reboot. When cross-building, this value may be different from the actual
|
||||
* version of Scala being used to build the project. ScalaVersion.current and ScalaVersion.cross should be used
|
||||
* to read the version of Scala building the project. This should only be used to change the version of Scala used
|
||||
* for normal development (not cross-building)*/
|
||||
final val scalaVersion = propertyOptional[String]("", true)
|
||||
final val sbtVersion = propertyOptional[String]("", true)
|
||||
/** The property that defines the version of Scala to use with the project definition. This can be different
|
||||
* from the version of Scala used to build the project (defined initially by buildInitScalaVersion).
|
||||
* This property is only read by `sbt` on startup and reload. It is the definitive source for the version of Scala
|
||||
* that sbt and the project definition are using.*/
|
||||
final val scalaVersion = property[String]
|
||||
final val sbtVersion = property[String]
|
||||
final val projectInitialize = propertyOptional[Boolean](false)
|
||||
final val projectScratch = propertyOptional[Boolean](false, true)
|
||||
/** The property that defines the version of Scala to build this project with by default. This can be
|
||||
* different from the version of Scala used to build and run the project definition (defined by scalaVersion).
|
||||
* This property is only read by `sbt` on startup and reload. When cross-building, this value may be different from the actual
|
||||
* version of Scala being used to build the project. info.scalaVersion is always the definitive source for the current Scala version.
|
||||
* This property should only be used to change the version of Scala used for normal development (not cross-building).*/
|
||||
final val buildInitScalaVersion = propertyOptional[String](scalaVersion.value, true)
|
||||
/** The definitive source for the version of Scala being used to *build* the project.*/
|
||||
def buildScalaVersion = info.buildScalaVersion.getOrElse(buildInitScalaVersion.value)
|
||||
|
||||
/** If this project is cross-building, returns `base` with an additional path component containing the scala version.
|
||||
* Otherwise, this returns `base`.
|
||||
def componentManager = new xsbt.ComponentManager(info.app.components, log)
|
||||
def buildScalaInstance =
|
||||
localScalaInstances.find(_.version == buildScalaVersion) getOrElse
|
||||
xsbt.ScalaInstance(buildScalaVersion, info.launcher)
|
||||
lazy val localScalaInstances: Seq[ScalaInstance] = localScala ++ info.parent.toList.flatMap(_.localScalaInstances)
|
||||
def localScala: Seq[ScalaInstance] = Nil
|
||||
def buildCompiler = new AnalyzingCompiler(buildScalaInstance, componentManager)
|
||||
def defineScala(home: File): ScalaInstance = ScalaInstance(home, info.launcher)
|
||||
def defineScala(version: String, home: File): ScalaInstance = ScalaInstance(version, home, info.launcher)
|
||||
|
||||
/** If this project is cross-building, returns `base` with an additional path component containing the scala version
|
||||
* currently used to build the project. Otherwise, this returns `base`.
|
||||
* By default, cross-building is enabled when a project is loaded by the loader and crossScalaVersions is not empty.*/
|
||||
def crossPath(base: Path) = ScalaVersion.withCross(disableCrossPaths)(base / ScalaVersion.crossString(_), base)
|
||||
def crossPath(base: Path) = if(disableCrossPaths) base else base / crossString
|
||||
/** If modifying paths for cross-building is enabled, this returns ScalaVersion.currentString.
|
||||
* Otherwise, this returns the empty string. */
|
||||
def crossScalaVersionString: String = if(disableCrossPaths) "" else ScalaVersion.currentString
|
||||
|
||||
def crossScalaVersionString: String = if(disableCrossPaths) "" else buildScalaVersion
|
||||
private def crossString = "scala_" + buildScalaVersion
|
||||
|
||||
|
||||
/** True if crossPath should be the identity function.*/
|
||||
protected def disableCrossPaths = crossScalaVersions.isEmpty
|
||||
/** By default, this is empty and cross-building is disabled. Overriding this to a Set of Scala versions
|
||||
|
|
@ -241,15 +262,15 @@ trait Project extends TaskManager with Dag[Project] with BasicEnvironment
|
|||
* project. This project does not need to include the watched paths for projects that this project depends on.*/
|
||||
def watchPaths: PathFinder = Path.emptyPathFinder
|
||||
def terminateWatch(key: Int): Boolean = key == 10 || key == 13
|
||||
|
||||
|
||||
protected final override def parentEnvironment = info.parent
|
||||
|
||||
|
||||
// .* included because svn doesn't mark .svn hidden
|
||||
def defaultExcludes: FileFilter = (".*" - ".") || HiddenFileFilter
|
||||
/** Short for parent.descendentsExcept(include, defaultExcludes)*/
|
||||
def descendents(parent: PathFinder, include: FileFilter) = parent.descendentsExcept(include, defaultExcludes)
|
||||
override def toString = "Project " + projectName.get.getOrElse("at " + environmentLabel)
|
||||
|
||||
|
||||
def normalizedName = StringUtilities.normalize(name)
|
||||
}
|
||||
private[sbt] sealed trait LoadResult extends NotNull
|
||||
|
|
@ -265,14 +286,14 @@ object Project
|
|||
val DefaultEnvBackingName = "build.properties"
|
||||
val DefaultBuilderClassName = "sbt.DefaultProject"
|
||||
val DefaultBuilderClass = Class.forName(DefaultBuilderClassName).asSubclass(classOf[Project])
|
||||
|
||||
|
||||
/** The name of the directory for project definitions.*/
|
||||
val BuilderProjectDirectoryName = "build"
|
||||
/** The name of the directory for plugin definitions.*/
|
||||
val PluginProjectDirectoryName = "plugins"
|
||||
/** The name of the class that all projects must inherit from.*/
|
||||
val ProjectClassName = classOf[Project].getName
|
||||
|
||||
|
||||
/** The logger that should be used before the root project definition is loaded.*/
|
||||
private[sbt] def bootLogger =
|
||||
{
|
||||
|
|
@ -283,20 +304,22 @@ object Project
|
|||
}
|
||||
|
||||
private[sbt] def booted = java.lang.Boolean.getBoolean("sbt.boot")
|
||||
|
||||
|
||||
private[sbt] def loadProject(app: AppProvider): LoadResult = loadProject(app, None)
|
||||
/** Loads the project in the current working directory. */
|
||||
private[sbt] def loadProject(app: AppProvider, buildScalaVersion: Option[String]): LoadResult = loadProject(bootLogger, app, buildScalaVersion)
|
||||
/** Loads the project in the current working directory.*/
|
||||
private[sbt] def loadProject: LoadResult = loadProject(bootLogger)
|
||||
/** Loads the project in the current working directory.*/
|
||||
private[sbt] def loadProject(log: Logger): LoadResult = checkOutputDirectories(loadProject(new File("."), Nil, None, log))
|
||||
private[sbt] def loadProject(log: Logger, app: AppProvider, buildScalaVersion: Option[String]): LoadResult =
|
||||
checkOutputDirectories(loadProject(new File("."), Nil, None, log, app, buildScalaVersion))
|
||||
/** Loads the project in the directory given by 'path' and with the given dependencies.*/
|
||||
private[sbt] def loadProject(path: Path, deps: Iterable[Project], parent: Option[Project], log: Logger): LoadResult =
|
||||
loadProject(path.asFile, deps, parent, log)
|
||||
private[sbt] def loadProject(path: Path, deps: Iterable[Project], parent: Option[Project], log: Logger, app: AppProvider, buildScalaVersion: Option[String]): LoadResult =
|
||||
loadProject(path.asFile, deps, parent, log, app, buildScalaVersion)
|
||||
/** Loads the project in the directory given by 'projectDirectory' and with the given dependencies.*/
|
||||
private[sbt] def loadProject(projectDirectory: File, deps: Iterable[Project], parent: Option[Project], log: Logger): LoadResult =
|
||||
loadProject(projectDirectory, deps, parent, getClass.getClassLoader, log)
|
||||
private[sbt] def loadProject(projectDirectory: File, deps: Iterable[Project], parent: Option[Project], additional: ClassLoader, log: Logger): LoadResult =
|
||||
private[sbt] def loadProject(projectDirectory: File, deps: Iterable[Project], parent: Option[Project], log: Logger, app: AppProvider, buildScalaVersion: Option[String]): LoadResult =
|
||||
loadProject(projectDirectory, deps, parent, getClass.getClassLoader, log, app, buildScalaVersion)
|
||||
private[sbt] def loadProject(projectDirectory: File, deps: Iterable[Project], parent: Option[Project], additional: ClassLoader, log: Logger, app: AppProvider, buildScalaVersion: Option[String]): LoadResult =
|
||||
{
|
||||
val info = ProjectInfo(projectDirectory, deps, parent)(log)
|
||||
val info = ProjectInfo(projectDirectory, deps, parent)(log, app, buildScalaVersion)
|
||||
ProjectInfo.setup(info, log) match
|
||||
{
|
||||
case err: SetupError => new LoadSetupError(err.message)
|
||||
|
|
@ -377,7 +400,8 @@ object Project
|
|||
{
|
||||
val pluginProjectPath = info.builderPath / PluginProjectDirectoryName
|
||||
val additionalPaths = additional match { case u: URLClassLoader => u.getURLs.map(url => Path.fromFile(FileUtilities.toFile(url))); case _ => Array[Path]() }
|
||||
val builderProject = new BuilderProject(ProjectInfo(builderProjectPath.asFile, Nil, None)(buildLog), pluginProjectPath, additionalPaths, buildLog)
|
||||
val builderInfo = ProjectInfo(builderProjectPath.asFile, Nil, None)(buildLog, info.app, Some(info.definitionScalaVersion))
|
||||
val builderProject = new BuilderProject(builderInfo, pluginProjectPath, additionalPaths, buildLog)
|
||||
builderProject.compile.run.toLeft(()).right.flatMap { ignore =>
|
||||
builderProject.projectDefinition.right.map {
|
||||
case Some(definition) => getProjectClass[Project](definition, builderProject.projectClasspath, additional)
|
||||
|
|
@ -447,7 +471,7 @@ object Project
|
|||
require(projectClass.isAssignableFrom(builderClass), "Builder class '" + builderClass + "' does not extend " + projectClass.getName + ".")
|
||||
builderClass.asSubclass(projectClass).asInstanceOf[Class[P]]
|
||||
}
|
||||
|
||||
|
||||
/** Writes the project name and a separator to the project's log at the info level.*/
|
||||
def showProjectHeader(project: Project)
|
||||
{
|
||||
|
|
@ -456,7 +480,7 @@ object Project
|
|||
project.log.info(projectHeader)
|
||||
project.log.info("=" * projectHeader.length)
|
||||
}
|
||||
|
||||
|
||||
def rootProject(p: Project): Project =
|
||||
p.info.parent match
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,10 +4,16 @@
|
|||
package sbt
|
||||
|
||||
import java.io.File
|
||||
import xsbti.{AppProvider, ScalaProvider}
|
||||
import FileUtilities._
|
||||
|
||||
final case class ProjectInfo(projectDirectory: File, dependencies: Iterable[Project], parent: Option[Project])(log: Logger) extends NotNull
|
||||
// provider is for the build, not the build definition
|
||||
final case class ProjectInfo(projectDirectory: File, dependencies: Iterable[Project], parent: Option[Project])
|
||||
(log: Logger, val app: AppProvider, val buildScalaVersion: Option[String]) extends NotNull
|
||||
{
|
||||
def definitionScalaVersion = app.scalaProvider.version
|
||||
def launcher = app.scalaProvider.launcher
|
||||
|
||||
val logger = new FilterLogger(log)
|
||||
val projectPath: Path =
|
||||
{
|
||||
|
|
@ -34,7 +40,7 @@ object ProjectInfo
|
|||
{
|
||||
val MetadataDirectoryName = "project"
|
||||
private val DefaultOrganization = "empty"
|
||||
|
||||
|
||||
def setup(info: ProjectInfo, log: Logger): SetupResult =
|
||||
{
|
||||
val builderDirectory = info.builderPath.asFile
|
||||
|
|
@ -81,7 +87,7 @@ object ProjectInfo
|
|||
}
|
||||
private def verifyCreateProject(name: String, version: Version, organization: String): Boolean =
|
||||
confirmPrompt("Create new project " + name + " " + version + " with organization " + organization +" ?", true)
|
||||
|
||||
|
||||
private def confirmPrompt(question: String, defaultYes: Boolean) =
|
||||
{
|
||||
val choices = if(defaultYes) " (Y/n) " else " (y/N) "
|
||||
|
|
@ -89,7 +95,7 @@ object ProjectInfo
|
|||
val yes = "y" :: "yes" :: (if(defaultYes) List("") else Nil)
|
||||
yes.contains(answer.toLowerCase)
|
||||
}
|
||||
|
||||
|
||||
private def readVersion(projectDirectory: File, log: Logger): Option[Version] =
|
||||
{
|
||||
val version = trim(SimpleReader.readLine("Version: "))
|
||||
|
|
|
|||
|
|
@ -4,32 +4,34 @@
|
|||
package sbt
|
||||
|
||||
import java.io.File
|
||||
import xsbti.AppProvider
|
||||
import FileUtilities._
|
||||
|
||||
object Resources
|
||||
{
|
||||
def apply(basePath: String) =
|
||||
def apply(basePath: String, provider: AppProvider, buildScalaVersion: Option[String]) =
|
||||
{
|
||||
require(basePath.startsWith("/"))
|
||||
val resource = getClass.getResource(basePath)
|
||||
if(resource == null)
|
||||
throw new Exception("Resource base directory '" + basePath + "' not on classpath.")
|
||||
error("Resource base directory '" + basePath + "' not on classpath.")
|
||||
else
|
||||
{
|
||||
val file = toFile(resource)
|
||||
if(file.exists)
|
||||
new Resources(file)
|
||||
new Resources(file, provider, buildScalaVersion)
|
||||
else
|
||||
throw new Exception("Resource base directory '" + basePath + "' does not exist.")
|
||||
error("Resource base directory '" + basePath + "' does not exist.")
|
||||
}
|
||||
}
|
||||
private val LoadErrorPrefix = "Error loading initial project: "
|
||||
}
|
||||
|
||||
class Resources(val baseDirectory: File, additional: ClassLoader)
|
||||
class Resources(val baseDirectory: File, additional: ClassLoader, app: AppProvider, buildScalaVersion: Option[String])
|
||||
{
|
||||
def this(baseDirectory: File) = this(baseDirectory, getClass.getClassLoader)
|
||||
|
||||
def this(baseDirectory: File, provider: AppProvider, buildScalaVersion: Option[String]) =
|
||||
this(baseDirectory, getClass.getClassLoader, provider, buildScalaVersion)
|
||||
|
||||
import Resources._
|
||||
// The returned directory is not actually read-only, but it should be treated that way
|
||||
def readOnlyResourceDirectory(group: String, name: String): Either[String, File] =
|
||||
|
|
@ -62,7 +64,7 @@ class Resources(val baseDirectory: File, additional: ClassLoader)
|
|||
}
|
||||
doInTemporaryDirectory(log)(readWrite(readOnly))
|
||||
}
|
||||
|
||||
|
||||
def withProject[T](projectDirectory: File, log: Logger)(f: Project => WithProjectResult[T]): Either[String, T] =
|
||||
readWriteResourceDirectory(projectDirectory, log)(withProject(log)(f))
|
||||
def withProject[T](group: String, name: String, log: Logger)(f: Project => WithProjectResult[T]): Either[String, T] =
|
||||
|
|
@ -87,7 +89,7 @@ class Resources(val baseDirectory: File, additional: ClassLoader)
|
|||
Left(msg)
|
||||
}
|
||||
buffered.recordAll()
|
||||
resultToEither(Project.loadProject(dir, Nil, None, additional, buffered)) match
|
||||
resultToEither(Project.loadProject(dir, Nil, None, additional, buffered, app, buildScalaVersion)) match
|
||||
{
|
||||
case Left(msg) =>
|
||||
reload match
|
||||
|
|
|
|||
|
|
@ -13,17 +13,10 @@ import java.net.{URL, URLClassLoader}
|
|||
|
||||
trait ScalaRun
|
||||
{
|
||||
def console(classpath: Iterable[Path], log: Logger): Option[String]
|
||||
def run(mainClass: String, classpath: Iterable[Path], options: Seq[String], log: Logger): Option[String]
|
||||
}
|
||||
class ForkRun(config: ForkScalaRun) extends ScalaRun
|
||||
{
|
||||
def console(classpath: Iterable[Path], log: Logger): Option[String] =
|
||||
{
|
||||
error("Forking the interpreter is not implemented.")
|
||||
//val exitCode = Fork.scala(config.javaHome, config.runJVMOptions, config.scalaJars, classpathOption(classpath), config.workingDirectory, log)
|
||||
//processExitCode(exitCode, "interpreter")
|
||||
}
|
||||
def run(mainClass: String, classpath: Iterable[Path], options: Seq[String], log: Logger): Option[String] =
|
||||
{
|
||||
val scalaOptions = classpathOption(classpath) ::: mainClass :: options.toList
|
||||
|
|
@ -42,31 +35,21 @@ class ForkRun(config: ForkScalaRun) extends ScalaRun
|
|||
Some("Nonzero exit code returned from " + label + ": " + exitCode)
|
||||
}
|
||||
}
|
||||
class Run(compiler: xsbt.AnalyzingCompiler) extends ScalaRun
|
||||
{
|
||||
/** Runs the class 'mainClass' using the given classpath and options using the scala runner.*/
|
||||
def run(mainClass: String, classpath: Iterable[Path], options: Seq[String], log: Logger) =
|
||||
{
|
||||
def execute = compiler.run(Set() ++ classpath.map(_.asFile), mainClass, options, log)
|
||||
Run.executeTrapExit( execute, log )
|
||||
}
|
||||
}
|
||||
|
||||
/** This module is an interface to starting the scala interpreter or runner.*/
|
||||
object Run extends ScalaRun
|
||||
object Run
|
||||
{
|
||||
/** Starts an interactive scala interpreter session with the given classpath.*/
|
||||
def console(classpath: Iterable[Path], log: Logger) =
|
||||
createSettings(log)
|
||||
{
|
||||
(settings: Settings) =>
|
||||
{
|
||||
settings.classpath.value = Path.makeString(classpath)
|
||||
log.info("Starting scala interpreter...")
|
||||
log.debug(" Classpath: " + settings.classpath.value)
|
||||
log.info("")
|
||||
Control.trapUnit("Error during session: ", log)
|
||||
{
|
||||
JLine.withJLine {
|
||||
val loop = new InterpreterLoop
|
||||
executeTrapExit(loop.main(settings), log)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Executes the given function, trapping calls to System.exit. */
|
||||
private def executeTrapExit(f: => Unit, log: Logger): Option[String] =
|
||||
private[sbt] def executeTrapExit(f: => Unit, log: Logger): Option[String] =
|
||||
{
|
||||
val exitCode = TrapExit(f, log)
|
||||
if(exitCode == 0)
|
||||
|
|
@ -77,37 +60,6 @@ object Run extends ScalaRun
|
|||
else
|
||||
Some("Nonzero exit code: " + exitCode)
|
||||
}
|
||||
/** Runs the class 'mainClass' using the given classpath and options using the scala runner.*/
|
||||
def run(mainClass: String, classpath: Iterable[Path], options: Seq[String], log: Logger) =
|
||||
{
|
||||
createSettings(log) { (settings: Settings) =>
|
||||
Control.trapUnit("Error during run: ", log)
|
||||
{
|
||||
val classpathURLs = classpath.map(_.asURL).toList
|
||||
val bootClasspath = FileUtilities.pathSplit(settings.bootclasspath.value)
|
||||
val extraURLs =
|
||||
for(pathString <- bootClasspath if pathString.length > 0) yield
|
||||
(new java.io.File(pathString)).toURI.toURL
|
||||
log.info("Running " + mainClass + " ...")
|
||||
log.debug(" Classpath:" + (classpathURLs ++ extraURLs).mkString("\n\t", "\n\t",""))
|
||||
def execute =
|
||||
try { ObjectRunner.run(classpathURLs ++ extraURLs, mainClass, options.toList) }
|
||||
catch { case e: java.lang.reflect.InvocationTargetException => throw e.getCause }
|
||||
executeTrapExit( execute, log )
|
||||
}
|
||||
}
|
||||
}
|
||||
/** If mainClassOption is None, then the interactive scala interpreter is started with the given classpath.
|
||||
* Otherwise, the class wrapped by Some is run using the scala runner with the given classpath and
|
||||
* options. */
|
||||
def apply(mainClassOption: Option[String], classpath: Iterable[Path], options: Seq[String], log: Logger) =
|
||||
{
|
||||
mainClassOption match
|
||||
{
|
||||
case Some(mainClass) => run(mainClass, classpath, options, log)
|
||||
case None => console(classpath, log)
|
||||
}
|
||||
}
|
||||
/** Create a settings object and execute the provided function if the settings are created ok.*/
|
||||
private def createSettings(log: Logger)(f: Settings => Option[String]) =
|
||||
{
|
||||
|
|
@ -140,8 +92,9 @@ object Run extends ScalaRun
|
|||
}}
|
||||
}
|
||||
/** A custom InterpreterLoop with the purpose of creating an interpreter with Project 'project' bound to the value 'current',
|
||||
* and the following two lines interpreted:
|
||||
* and the following three lines interpreted:
|
||||
* import sbt._
|
||||
* import Process._
|
||||
* import current._.
|
||||
* To do this,
|
||||
* 1) The compiler uses a different settings instance: 'compilerSettings', which will have its classpath set to include the classpath
|
||||
|
|
|
|||
|
|
@ -11,14 +11,14 @@ import scala.collection.mutable.ListBuffer
|
|||
trait SimpleScalaProject extends ExecProject
|
||||
{
|
||||
def errorTask(message: String) = task{ Some(message) }
|
||||
|
||||
|
||||
trait CleanOption extends ActionOption
|
||||
case class ClearAnalysis(analysis: TaskAnalysis[_, _, _]) extends CleanOption
|
||||
case class Preserve(paths: PathFinder) extends CleanOption
|
||||
|
||||
|
||||
case class CompileOption(val asString: String) extends ActionOption
|
||||
case class JavaCompileOption(val asString: String) extends ActionOption
|
||||
|
||||
|
||||
val Deprecation = CompileOption("-deprecation")
|
||||
val ExplainTypes = CompileOption("-explaintypes")
|
||||
val Optimize = CompileOption("-optimise")
|
||||
|
|
@ -33,7 +33,7 @@ trait SimpleScalaProject extends ExecProject
|
|||
val Java1_4 = Value("jvm-1.4")
|
||||
val Msil = Value("msil")
|
||||
}
|
||||
|
||||
|
||||
def cleanTask(paths: PathFinder, options: CleanOption*): Task =
|
||||
cleanTask(paths, options)
|
||||
def cleanTask(paths: PathFinder, options: => Seq[CleanOption]): Task =
|
||||
|
|
@ -57,17 +57,17 @@ trait SimpleScalaProject extends ExecProject
|
|||
trait ScalaProject extends SimpleScalaProject with FileTasks with MultiTaskProject with Exec
|
||||
{
|
||||
import ScalaProject._
|
||||
|
||||
|
||||
final case class MaxCompileErrors(val value: Int) extends CompileOption("") with ScaladocOption { def asList = Nil }
|
||||
trait PackageOption extends ActionOption
|
||||
trait TestOption extends ActionOption
|
||||
|
||||
|
||||
case class TestSetup(setup: () => Option[String]) extends TestOption
|
||||
case class TestCleanup(cleanup: () => Option[String]) extends TestOption
|
||||
case class ExcludeTests(tests: Iterable[String]) extends TestOption
|
||||
case class TestListeners(listeners: Iterable[TestReportListener]) extends TestOption
|
||||
case class TestFilter(filterTest: String => Boolean) extends TestOption
|
||||
|
||||
|
||||
case class JarManifest(m: Manifest) extends PackageOption
|
||||
{
|
||||
assert(m != null)
|
||||
|
|
@ -80,8 +80,8 @@ trait ScalaProject extends SimpleScalaProject with FileTasks with MultiTaskProje
|
|||
val converted = for( (name,value) <- attributes ) yield (new Attributes.Name(name), value)
|
||||
new ManifestAttributes(converted : _*)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
trait ScaladocOption extends ActionOption
|
||||
{
|
||||
def asList: List[String]
|
||||
|
|
@ -105,7 +105,7 @@ trait ScalaProject extends SimpleScalaProject with FileTasks with MultiTaskProje
|
|||
def stylesheetFile(path: Path) = CompoundDocOption("-stylesheetfile", path.asFile.getAbsolutePath)
|
||||
def documentTop(topText: String) = CompoundDocOption("-top", topText)
|
||||
def windowTitle(title: String) = CompoundDocOption("-windowtitle", title)
|
||||
|
||||
|
||||
object Access extends Enumeration
|
||||
{
|
||||
val Public = Value("public")
|
||||
|
|
@ -123,17 +123,16 @@ trait ScalaProject extends SimpleScalaProject with FileTasks with MultiTaskProje
|
|||
val classes = conditional.analysis.allProducts.flatMap(path => Path.relativize(compilePath.asFile, path.asFile))
|
||||
classes.map(_.replace(java.io.File.separatorChar, '.').toList.dropRight(".class".length).mkString).toSeq
|
||||
}
|
||||
|
||||
def consoleTask(classpath : PathFinder): Task =
|
||||
consoleTask(classpath, Run)
|
||||
def consoleTask(classpath : PathFinder, runner: ScalaRun): Task =
|
||||
interactiveTask { runner.console(classpath.get, log) }
|
||||
|
||||
def runTask(mainClass: => Option[String], classpath: PathFinder, options: String*): Task =
|
||||
def consoleTask(classpath: PathFinder): Task = consoleTask(classpath, "")
|
||||
def consoleTask(classpath: PathFinder, initialCommands: => String): Task =
|
||||
interactiveTask {
|
||||
(new Console(buildCompiler))(classpath.get, initialCommands, log)
|
||||
}
|
||||
|
||||
def runTask(mainClass: => Option[String], classpath: PathFinder, options: String*)(implicit runner: ScalaRun): Task =
|
||||
runTask(mainClass, classpath, options)
|
||||
def runTask(mainClass: => Option[String], classpath: PathFinder, options: => Seq[String]): Task =
|
||||
runTask(mainClass, classpath, options, Run)
|
||||
def runTask(mainClass: => Option[String], classpath: PathFinder, options: => Seq[String], runner: ScalaRun): Task =
|
||||
def runTask(mainClass: => Option[String], classpath: PathFinder, options: => Seq[String])(implicit runner: ScalaRun): Task =
|
||||
task
|
||||
{
|
||||
mainClass match
|
||||
|
|
@ -142,7 +141,7 @@ trait ScalaProject extends SimpleScalaProject with FileTasks with MultiTaskProje
|
|||
case None => Some("No main class specified.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def syncPathsTask(sources: PathFinder, destinationDirectory: Path): Task =
|
||||
task { FileUtilities.syncPaths(sources, destinationDirectory, log) }
|
||||
def syncTask(sourceDirectory: Path, destinationDirectory: Path): Task =
|
||||
|
|
@ -177,10 +176,9 @@ trait ScalaProject extends SimpleScalaProject with FileTasks with MultiTaskProje
|
|||
def scaladocTask(label: String, sources: PathFinder, outputDirectory: Path, classpath: PathFinder, options: => Seq[ScaladocOption]): Task =
|
||||
task
|
||||
{
|
||||
val classpathString = Path.makeString(classpath.get)
|
||||
val optionsLocal = options
|
||||
val maxErrors = maximumErrors(optionsLocal)
|
||||
(new Scaladoc(maxErrors))(label, sources.get, classpathString, outputDirectory, optionsLocal.flatMap(_.asList), log)
|
||||
(new Scaladoc(maxErrors, buildCompiler))(label, sources.get, classpath.get, outputDirectory, optionsLocal.flatMap(_.asList), log)
|
||||
}
|
||||
|
||||
def packageTask(sources: PathFinder, outputDirectory: Path, jarName: => String, options: PackageOption*): Task =
|
||||
|
|
@ -206,7 +204,7 @@ trait ScalaProject extends SimpleScalaProject with FileTasks with MultiTaskProje
|
|||
{
|
||||
option match
|
||||
{
|
||||
case JarManifest(mergeManifest) =>
|
||||
case JarManifest(mergeManifest) =>
|
||||
{
|
||||
mergeAttributes(manifest.getMainAttributes, mergeManifest.getMainAttributes)
|
||||
val entryMap = new MutableMapWrapper(manifest.getEntries)
|
||||
|
|
@ -258,7 +256,7 @@ trait ScalaProject extends SimpleScalaProject with FileTasks with MultiTaskProje
|
|||
val excludeTestsSet = new HashSet[String]
|
||||
val setup, cleanup = new ListBuffer[() => Option[String]]
|
||||
val testListeners = new ListBuffer[TestReportListener]
|
||||
|
||||
|
||||
for(option <- options)
|
||||
{
|
||||
option match
|
||||
|
|
@ -271,7 +269,7 @@ trait ScalaProject extends SimpleScalaProject with FileTasks with MultiTaskProje
|
|||
}
|
||||
() // 2.8.0-SNAPSHOT bug in type inference
|
||||
}
|
||||
|
||||
|
||||
if(excludeTestsSet.size > 0 && log.atLevel(Level.Debug))
|
||||
{
|
||||
log.debug("Excluding tests: ")
|
||||
|
|
@ -282,12 +280,12 @@ trait ScalaProject extends SimpleScalaProject with FileTasks with MultiTaskProje
|
|||
TestFramework.testTasks(frameworks, classpath.get, tests, log, testListeners.readOnly, false, setup.readOnly, cleanup.readOnly)
|
||||
}
|
||||
private def flatten[T](i: Iterable[Iterable[T]]) = i.flatMap(x => x)
|
||||
|
||||
|
||||
protected def testQuickMethod(testAnalysis: CompileAnalysis, options: => Seq[TestOption])(toRun: Seq[TestOption] => Task) =
|
||||
multiTask(testAnalysis.allTests.map(_.testClassName).toList) { includeFunction =>
|
||||
toRun(TestFilter(includeFunction) :: options.toList)
|
||||
}
|
||||
|
||||
|
||||
protected final def maximumErrors[T <: ActionOption](options: Seq[T]) =
|
||||
(for( MaxCompileErrors(maxErrors) <- options) yield maxErrors).firstOption.getOrElse(DefaultMaximumCompileErrors)
|
||||
}
|
||||
|
|
@ -301,12 +299,12 @@ trait WebScalaProject extends ScalaProject
|
|||
val webInfPath = warPath / "WEB-INF"
|
||||
val webLibDirectory = webInfPath / "lib"
|
||||
val classesTargetDirectory = webInfPath / "classes"
|
||||
|
||||
|
||||
val (libs, directories) = classpath.get.toList.partition(ClasspathUtilities.isArchive)
|
||||
val classesAndResources = descendents(Path.lazyPathFinder(directories) ##, "*")
|
||||
if(log.atLevel(Level.Debug))
|
||||
directories.foreach(d => log.debug(" Copying the contents of directory " + d + " to " + classesTargetDirectory))
|
||||
|
||||
|
||||
import FileUtilities.{copy, copyFlat, copyFilesFlat, clean}
|
||||
(copy(webappContents.get, warPath, log).right flatMap { copiedWebapp =>
|
||||
copy(classesAndResources.get, classesTargetDirectory, log).right flatMap { copiedClasses =>
|
||||
|
|
@ -370,7 +368,7 @@ trait MultiTaskProject extends Project
|
|||
filterInclude
|
||||
run(includeFunction)
|
||||
} completeWith allTests
|
||||
|
||||
|
||||
}
|
||||
trait ExecProject extends Project
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,47 +0,0 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2009 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
|
||||
/** Provides access to the current version of Scala being used to build a project. These methods typically
|
||||
* return None or the empty string when the loader is not used. */
|
||||
object ScalaVersion
|
||||
{
|
||||
/** The name of the system property containing the Scala version used for this project.*/
|
||||
private[sbt] val LiveKey = "sbt.scala.version"
|
||||
private[sbt] def crossString(v: String) = "scala_" + v
|
||||
/** Returns the current version of Scala being used to build the project, unless the sbt loader is not being used,
|
||||
* in which case this is the empty string.*/
|
||||
def currentString =
|
||||
{
|
||||
val v = System.getProperty(LiveKey)
|
||||
if(v == null)
|
||||
""
|
||||
else
|
||||
v.trim
|
||||
}
|
||||
/** Returns the current version of Scala being used to build the project. If the sbt loader is not being
|
||||
* used, this returns None. Otherwise, the value returned by this method is fixed for the duration of
|
||||
* a Project's existence. It only changes on reboot (during which a Project is recreated).*/
|
||||
val current: Option[String] =
|
||||
{
|
||||
val sv = currentString
|
||||
if(sv.isEmpty)
|
||||
None
|
||||
else
|
||||
Some(sv)
|
||||
}
|
||||
private[sbt] def withCross[T](crossDisabled: Boolean)(withVersion: String => T, disabled: => T): T =
|
||||
{
|
||||
if(crossDisabled)
|
||||
disabled
|
||||
else
|
||||
{
|
||||
current match
|
||||
{
|
||||
case Some(scalaV) => withVersion(scalaV)
|
||||
case _ => disabled
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@ object JettyRunner
|
|||
class JettyRunner(configuration: JettyConfiguration) extends ExitHook
|
||||
{
|
||||
ExitHooks.register(this)
|
||||
|
||||
|
||||
def name = "jetty-shutdown"
|
||||
def runBeforeExiting() { stop() }
|
||||
private var running: Option[Stoppable] = None
|
||||
|
|
@ -37,7 +37,7 @@ class JettyRunner(configuration: JettyConfiguration) extends ExitHook
|
|||
val runner = ModuleUtilities.getObject(implClassName, lazyLoader).asInstanceOf[JettyRun]
|
||||
runner(configuration)
|
||||
}
|
||||
|
||||
|
||||
if(running.isDefined)
|
||||
Some("This instance of Jetty is already running.")
|
||||
else
|
||||
|
|
@ -55,7 +55,7 @@ class JettyRunner(configuration: JettyConfiguration) extends ExitHook
|
|||
}
|
||||
}
|
||||
private val implClassName = "sbt.LazyJettyRun"
|
||||
|
||||
|
||||
private def runError(e: Throwable, messageBase: String, log: Logger) =
|
||||
{
|
||||
log.trace(e)
|
||||
|
|
@ -78,8 +78,6 @@ sealed trait JettyConfiguration extends NotNull
|
|||
def scanInterval: Int
|
||||
/** The classpath to get Jetty from. */
|
||||
def jettyClasspath: PathFinder
|
||||
/** The classpath containing the classes, jars, and resources for the web application. */
|
||||
def classpath: PathFinder
|
||||
def classpathName: String
|
||||
def log: Logger
|
||||
}
|
||||
|
|
@ -87,6 +85,9 @@ trait DefaultJettyConfiguration extends JettyConfiguration
|
|||
{
|
||||
def contextPath: String
|
||||
def port: Int
|
||||
/** The classpath containing the classes, jars, and resources for the web application. */
|
||||
def classpath: PathFinder
|
||||
def parentLoader: ClassLoader
|
||||
}
|
||||
abstract class CustomJettyConfiguration extends JettyConfiguration
|
||||
{
|
||||
|
|
@ -108,17 +109,17 @@ private object LazyJettyRun extends JettyRun
|
|||
import org.mortbay.log.Log
|
||||
import org.mortbay.util.Scanner
|
||||
import org.mortbay.xml.XmlConfiguration
|
||||
|
||||
|
||||
import java.lang.ref.{Reference, WeakReference}
|
||||
|
||||
|
||||
val DefaultMaxIdleTime = 30000
|
||||
|
||||
|
||||
def apply(configuration: JettyConfiguration): Stoppable =
|
||||
{
|
||||
val oldLog = Log.getLog
|
||||
Log.setLog(new JettyLogger(configuration.log))
|
||||
val server = new Server
|
||||
|
||||
|
||||
val listener =
|
||||
configuration match
|
||||
{
|
||||
|
|
@ -126,11 +127,11 @@ private object LazyJettyRun extends JettyRun
|
|||
import c._
|
||||
configureDefaultConnector(server, port)
|
||||
def classpathURLs = classpath.get.map(_.asURL).toSeq
|
||||
def createLoader = new URLClassLoader(classpathURLs.toArray, this.getClass.getClassLoader)
|
||||
def createLoader = new URLClassLoader(classpathURLs.toArray, parentLoader)
|
||||
val webapp = new WebAppContext(war.absolutePath, contextPath)
|
||||
webapp.setClassLoader(createLoader)
|
||||
server.setHandler(webapp)
|
||||
|
||||
|
||||
Some(new Scanner.BulkListener {
|
||||
def filesChanged(files: java.util.List[_]) {
|
||||
reload(server, webapp.setClassLoader(createLoader), log)
|
||||
|
|
@ -143,7 +144,7 @@ private object LazyJettyRun extends JettyRun
|
|||
(new XmlConfiguration(file.toURI.toURL)).configure(server)
|
||||
None
|
||||
}
|
||||
|
||||
|
||||
def configureScanner() =
|
||||
{
|
||||
val scanDirectories = configuration.scanDirectories
|
||||
|
|
@ -164,7 +165,7 @@ private object LazyJettyRun extends JettyRun
|
|||
Some(new WeakReference(scanner))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
server.start()
|
||||
|
|
@ -219,7 +220,7 @@ private object LazyJettyRun extends JettyRun
|
|||
{
|
||||
def isDebugEnabled = delegate.atLevel(Level.Debug)
|
||||
def setDebugEnabled(enabled: Boolean) = delegate.setLevel(if(enabled) Level.Debug else Level.Info)
|
||||
|
||||
|
||||
def getLogger(name: String) = this
|
||||
def info(msg: String, arg0: AnyRef, arg1: AnyRef) { delegate.info(format(msg, arg0, arg1)) }
|
||||
def debug(msg: String, arg0: AnyRef, arg1: AnyRef) { delegate.debug(format(msg, arg0, arg1)) }
|
||||
|
|
|
|||
Loading…
Reference in New Issue