Project root path, defaults to current directory.
Use `resolve-path` to resolve relative paths in terms of the *project-root*.
Use `set-project-root!` to override the default for all tasks.
compile-clj
(compile-clj params)
Compile Clojure source to classes in :class-dir.
Clojure source files are found in :basis :paths by default, or override with :src-dirs.
Namespaces and order of compilation are one of:
* :ns-compile - compile these namespaces, in this order
* :sort - find all namespaces in source files and use either :topo (default)
or :bfs to order them for compilation
Options:
:basis - required, basis to use when compiling
:class-dir - required, dir to write classes, will be created if needed
:src-dirs - coll of Clojure source dirs, used to find all Clojure nses to compile
:ns-compile - coll of specific namespace symbols to compile
:sort - :topo (default) or :bfs for breadth-first search
:compile-opts - map of Clojure compiler options:
{:disable-locals-clearing false
:elide-meta [:doc :file :line ...]
:direct-linking false}
:bindings - map of Var to value to be set during compilation, for example:
{#'clojure.core/*assert* false
#'clojure.core/*warn-on-reflection* true}
:filter-nses - coll of symbols representing a namespace prefix to include
Additional options flow to the forked process doing the compile:
:java-cmd - Java command, default = $JAVA_CMD or 'java' on $PATH, or $JAVA_HOME/bin/java
:java-opts - coll of string jvm opts
:use-cp-file - one of:
:auto (default) - use only if os=windows && Java >= 9 && command length >= 8k
:always - always write classpath to temp file and include
:never - never write classpath to temp file (pass on command line)
:out - one of :inherit :capture :write :append :ignore
:err - one of :inherit :capture :write :append :ignore
:out-file - file path to write if :out is :write or :append
:err-file - file path to write if :err is :write or :append
Returns nil, or if needed a map with keys:
:out captured-out
:err captured-err
copy-dir
(copy-dir params)
Copy the contents of the src-dirs to the target-dir, optionally do text replacement.
Returns nil.
Globs are wildcard patterns for specifying sets of files in a directory
tree, as specified in the glob syntax of java.nio.file.FileSystem:
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/file/FileSystem.html#getPathMatcher(java.lang.String)
Options:
:target-dir - required, dir to write files, will be created if it doesn't exist
:src-dirs - required, coll of dirs to copy from
:include - glob of files to include, default = "**"
:ignores - collection of ignore regex patterns (applied only to file names),
see clojure.tools.build.tasks.copy/default-ignores for defaults
:replace - map of source to replacement string in files
:non-replaced-exts - coll of extensions to skip when replacing (still copied)
default = ["jpg" "jpeg" "png" "gif" "bmp"]
copy-file
(copy-file {:keys [src target], :as params})
Copy one file from source to target, creating target dirs if needed.
Returns nil.
Options:
:src - required, source path
:target - required, target path
create-basis
(create-basis)(create-basis params)
Create a basis from a set of deps sources and a set of aliases. By default, use
root, user, and project deps and no aliases (essentially the same classpath you
get by default from the Clojure CLI).
Each dep source value can be :standard, a string path, a deps edn map, or nil.
Sources are merged in the order - :root, :user, :project, :extra.
Options (note, paths resolved via *project-root*):
:dir - directory root path, defaults to current directory
:root - dep source, default = :standard
:user - dep source, default = nil (for reproducibility, not included)
:project - dep source, default = :standard ("./deps.edn")
:extra - dep source, default = nil
:aliases - coll of aliases of argmaps to apply to subprocesses
Returns a runtime basis, which is the initial merged deps edn map plus these keys:
:resolve-args - the resolve args passed in, if any
:classpath-args - the classpath args passed in, if any
:libs - lib map, per resolve-deps
:classpath - classpath map per make-classpath-map
:classpath-roots - vector of paths in classpath order
delete
(delete {:keys [path], :as params})
Delete file or directory recursively, if it exists. Returns nil.
Options:
:path - required, path to file or directory
Shells out to git and returns count of commits on this branch:
git rev-list HEAD --count
Options:
:dir - dir to invoke this command from, default = current directory
:git-command - git command to use, default = "git"
:path - path to count commits for relative to dir
git-process
(git-process params)
Run git process in the specified dir using git-command with git-args (which should not
start with "git"). git-args may either be a string (split on whitespace) or a vector
of strings. By default, stdout is captured, trimmed, and returned.
Options:
:dir - dir to invoke this command from, default = current directory
:git-command - git command to use, default = "git"
:git-args - required, coll of git-arg strings forming a command line OR
a string (do not use if args may have embedded spaces)
:capture - :out (default) or :err, else nothing
Examples:
(api/git-process {:git-args "rev-list HEAD --count"})
(api/git-process {:git-args "branch --show-current"})
(api/git-process {:git-args "rev-parse --short HEAD"})
(api/git-process {:git-args "push", :capture nil})
install
(install params)
Install pom and jar to local Maven repo.
Returns nil.
Options:
:basis - required, used for :mvn/local-repo
:lib - required, lib symbol
:classifier - classifier string, if needed
:version - required, string version
:jar-file - required, path to jar file
:class-dir - required, used to find the pom file
jar
(jar params)
Create jar file containing contents of class-dir. Use main in the manifest
if provided. Returns nil.
Options:
:class-dir - required, dir to include in jar
:jar-file - required, jar to write
:main - main class symbol
:manifest - map of manifest attributes, merged last over defaults+:main
java-command
(java-command params)
Create Java command line args. The classpath will be the combination of
:cp followed by the classpath from the basis, both are optional.
Note that 'java-command' will NOT resolve any relative paths from basis
or cp in terms of *project-root*, you will get a classpath with the same
relative paths. 'process' (if run with this output), will run in the
context of the *project-root* directory.
Options:
:java-cmd - Java command, default = $JAVA_CMD or 'java' on $PATH, or $JAVA_HOME/bin/java
:cp - coll of string classpath entries, used first (if provided)
:basis - runtime basis used for classpath and jvm opts from aliases, used last (if provided)
:java-opts - coll of string jvm opts
:main - required, main class symbol
:main-args - coll of main class args
:use-cp-file - one of:
:auto (default) - use only if os=windows && Java >= 9 && command length >= 8k
:always - always write classpath to temp file and include
:never - never write classpath to temp file (pass on command line)
Returns map suitable for passing to 'process' with keys:
:command-args - coll of command arg strings
javac
(javac params)
Compile Java source to classes. Returns nil.
Options:
:src-dirs - required, coll of Java source dirs
:class-dir - required, dir to write classes, will be created if needed
:basis - classpath basis to use when compiling
:javac-opts - coll of string opts, like ["-source" "8" "-target" "8"]
pom-path
(pom-path params)
Calculate path to pom.xml in jar meta (same path used by write-pom).
Relative path in jar is:
META-INF/maven/<groupId>/<artifactId>/pom.xml
If :class-dir provided, return path will start with resolved class-dir
(which may be either absolute or relative), otherwise just relative
path in jar.
Options:
:lib - required, used to form the relative path in jar to pom.xml
:class-dir - optional, if provided will be resolved and form the root of the path
process
(process params)
Exec the command made from command-args, redirect out and err as directed,
and return {:exit exit-code, :out captured-out, :err captured-err}.
Options:
:command-args - required, coll of string args
:dir - directory to run the command from, default *project-root*
:out - one of :inherit :capture :write :append :ignore
:err - one of :inherit :capture :write :append :ignore
:out-file - file path to write if :out is :write or :append
:err-file - file path to write if :err is :write or :append
:env - map of environment variables to set
The :out and :err input flags take one of the following options:
:inherit - inherit the stream and write the subprocess io to this process's stream (default)
:capture - capture the stream to a string and return it
:write - write to :out-file or :err-file
:append - append to :out-file or :err-file
:ignore - ignore the stream
resolve-path
(resolve-path path)
If path is absolute or root-path is nil then return path,
otherwise resolve relative to *project-root*.
set-project-root!
(set-project-root! root)
Set *project-root* dir (default is ".")
uber
(uber params)
Create uberjar file. An uberjar is a self-contained jar file containing
both the project contents AND the contents of all dependencies.
The project contents are represented by the class-dir. Use other tasks to
put Clojure source, class files, a pom file, or other resources in the
class-dir. In particular, see the copy-dir, write-pom, compile-clj, and
javac tasks.
The dependencies are pulled from the basis. All transitive deps will be
included. Dependency jars are expanded for inclusion in the uberjar.
Use :exclude to exclude specific paths from the expanded deps. Use
conflict-handlers to handle conflicts that may occur if two dependency
jar files include a file at the same path. See below for more detail.
If a main class or manifest are provided, those are put in the uberjar
META-INF/MANIFEST.MF file. Providing a main allows the jar to be
invoked with java -jar.
Returns nil.
Options:
:uber-file - required, uber jar file to create
:class-dir - required, local class dir to include
:basis - used to pull dep jars
:main - main class symbol
:manifest - map of manifest attributes, merged last over defaults + :main
:exclude - coll of string patterns (regex) to exclude from deps
:conflict-handlers - map of string pattern (regex) to built-in handlers,
symbols to eval, or function instances
When combining jar files into an uber jar, multiple jars may contain a file
at the same path. The conflict handlers are a map of string regex pattern
to:
a keyword (to use a built-in handler) or
a symbol (to resolve and invoke) or
a function instance
The special key `:default` specifies the default behavior if not matched.
Conflict handler signature (fn [params]) => effect-map:
params:
:path - String, path in uber jar, matched by regex
:in - InputStream to incoming file (see stream->string if needed)
:existing - File, existing File at path
:lib - symbol, lib source for incoming conflict
:state - map, available for retaining state during uberjar process
Handler should return effect-map with optional keys:
:state - updated state map
:write - map of string path to map of :string (string) or
:stream (InputStream) to write and optional :append
flag. Omit if no files to write.
Available built-in conflict handlers:
:ignore - don't do anything (default)
:overwrite - overwrite (replaces prior file)
:append - append the file with a blank line separator
:append-dedupe - append the file but dedupe appended sections
:data-readers - merge data_readers.clj
:warn - print a warning
:error - throw an error
Default conflict handlers map:
{"^data_readers.clj[c]?$" :data-readers
"^META-INF/services/.*" :append
"(?i)^(META-INF/)?(COPYRIGHT|NOTICE|LICENSE)(\\.(txt|md))?$" :append-dedupe
:default :ignore}
unzip
(unzip params)
Unzip zip file to target-dir. Returns nil.
Options:
:zip-file - required, zip file to unzip
:target-dir - required, directory to unzip in
with-project-root
macro
(with-project-root path & forms)
Execute forms in a bound project path (string) other than the default (".")
Writes a file at path, will create parent dirs if needed. Returns nil.
File contents may be specified either with :content (for data, that
will be pr-str'ed) or with :string for the string to write. If
neither is specified, an empty file is created (like touch).
Options:
:path - required, file path
:content - val to write, will pr-str
:string - string to write
:opts - coll of writer opts like :append and :encoding (per clojure.java.io)
write-pom
(write-pom params)
Write pom.xml and pom.properties files to the class dir under
META-INF/maven/group-id/artifact-id/ (where Maven typically writes
these files), or to target (exactly one of :class-dir and :target must
be provided).
Optionally use :src-pom to provide a pom template (or a default will
be generated from the provided attributes). The pom deps, dirs, and
repos from the basis will replace those sections of the template. Note
that the :src-pom template is not validated and should contain required
elements such as modelVersion.
If a repos map is provided it supersedes the repos in the basis.
Returns nil.
Options:
:basis - required, used to pull deps, repos
:src-pom - source pom.xml to synchronize from, default = "./pom.xml"
may be :none to ignore any existing pom.xml file
:class-dir - root dir for writing pom files, created if needed
:target - file path to write pom if no :class-dir specified
:lib - required, project lib symbol
:version - required, project version
:scm - map of scm properties to write in pom
keys: :connection, :developerConnection, :tag, :url
See: https://maven.apache.org/pom.html#SCM for details
:src-dirs - coll of src dirs
:resource-dirs - coll of resource dirs
:repos - map of repo name to repo config, replaces repos from deps.edn
:pom-data - vector of hiccup-style extra pom top elements to include when
:src-pom is :none or the source pom.xml does not exist:
[[:licenses
[:license
[:name "Eclipse Public License 1.0"]
[:url "https://opensource.org/license/epl-1-0/";]
[:distribution "repo"]]]
[:organization "Super Corp"]]
The pom-data MUST NOT include:
:modelVersion, :packaging, :groupId, :artifactId, :version, :name,
:deps, :repositories, :build, or :scm
zip
(zip params)
Create zip file containing contents of src dirs. Returns nil.
Options:
:src-dirs - required, coll of source directories to include in zip
:zip-file - required, zip file to create
================================================
FILE: docs/js/page_effects.js
================================================
function visibleInParent(element) {
var position = $(element).position().top
return position > -50 && position < ($(element).offsetParent().height() - 50)
}
function hasFragment(link, fragment) {
return $(link).attr("href").indexOf("#" + fragment) != -1
}
function findLinkByFragment(elements, fragment) {
return $(elements).filter(function(i, e) { return hasFragment(e, fragment)}).first()
}
function scrollToCurrentVarLink(elements) {
var elements = $(elements);
var parent = elements.offsetParent();
if (elements.length == 0) return;
var top = elements.first().position().top;
var bottom = elements.last().position().top + elements.last().height();
if (top >= 0 && bottom <= parent.height()) return;
if (top < 0) {
parent.scrollTop(parent.scrollTop() + top);
}
else if (bottom > parent.height()) {
parent.scrollTop(parent.scrollTop() + bottom - parent.height());
}
}
function setCurrentVarLink() {
$('.secondary a').parent().removeClass('current')
$('.anchor').
filter(function(index) { return visibleInParent(this) }).
each(function(index, element) {
findLinkByFragment(".secondary a", element.id).
parent().
addClass('current')
});
scrollToCurrentVarLink('.secondary .current');
}
var hasStorage = (function() { try { return localStorage.getItem } catch(e) {} }())
function scrollPositionId(element) {
var directory = window.location.href.replace(/[^\/]+\.html$/, '')
return 'scroll::' + $(element).attr('id') + '::' + directory
}
function storeScrollPosition(element) {
if (!hasStorage) return;
localStorage.setItem(scrollPositionId(element) + "::x", $(element).scrollLeft())
localStorage.setItem(scrollPositionId(element) + "::y", $(element).scrollTop())
}
function recallScrollPosition(element) {
if (!hasStorage) return;
$(element).scrollLeft(localStorage.getItem(scrollPositionId(element) + "::x"))
$(element).scrollTop(localStorage.getItem(scrollPositionId(element) + "::y"))
}
function persistScrollPosition(element) {
recallScrollPosition(element)
$(element).scroll(function() { storeScrollPosition(element) })
}
function sidebarContentWidth(element) {
var widths = $(element).find('.inner').map(function() { return $(this).innerWidth() })
return Math.max.apply(Math, widths)
}
function calculateSize(width, snap, margin, minimum) {
if (width == 0) {
return 0
}
else {
return Math.max(minimum, (Math.ceil(width / snap) * snap) + (margin * 2))
}
}
function resizeSidebars() {
var primaryWidth = sidebarContentWidth('.primary')
var secondaryWidth = 0
if ($('.secondary').length != 0) {
secondaryWidth = sidebarContentWidth('.secondary')
}
// snap to grid
primaryWidth = calculateSize(primaryWidth, 32, 13, 160)
secondaryWidth = calculateSize(secondaryWidth, 32, 13, 160)
$('.primary').css('width', primaryWidth)
$('.secondary').css('width', secondaryWidth).css('left', primaryWidth + 1)
if (secondaryWidth > 0) {
$('#content').css('left', primaryWidth + secondaryWidth + 2)
}
else {
$('#content').css('left', primaryWidth + 1)
}
}
$(window).ready(resizeSidebars)
$(window).ready(setCurrentVarLink)
$(window).ready(function() { persistScrollPosition('.primary')})
$(window).ready(function() {
$('#content').scroll(setCurrentVarLink)
$(window).resize(setCurrentVarLink)
})
================================================
FILE: epl-v10.html
================================================
Eclipse Public License - Version 1.0
Eclipse Public License - v 1.0
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR
DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS
AGREEMENT.
1. DEFINITIONS
"Contribution" means:
a) in the case of the initial Contributor, the initial
code and documentation distributed under this Agreement, and
b) in the case of each subsequent Contributor:
i) changes to the Program, and
ii) additions to the Program;
where such changes and/or additions to the Program
originate from and are distributed by that particular Contributor. A
Contribution 'originates' from a Contributor if it was added to the
Program by such Contributor itself or anyone acting on such
Contributor's behalf. Contributions do not include additions to the
Program which: (i) are separate modules of software distributed in
conjunction with the Program under their own license agreement, and (ii)
are not derivative works of the Program.
"Contributor" means any person or entity that distributes
the Program.
"Licensed Patents" mean patent claims licensable by a
Contributor which are necessarily infringed by the use or sale of its
Contribution alone or when combined with the Program.
"Program" means the Contributions distributed in accordance
with this Agreement.
"Recipient" means anyone who receives the Program under
this Agreement, including all Contributors.
2. GRANT OF RIGHTS
a) Subject to the terms of this Agreement, each
Contributor hereby grants Recipient a non-exclusive, worldwide,
royalty-free copyright license to reproduce, prepare derivative works
of, publicly display, publicly perform, distribute and sublicense the
Contribution of such Contributor, if any, and such derivative works, in
source code and object code form.
b) Subject to the terms of this Agreement, each
Contributor hereby grants Recipient a non-exclusive, worldwide,
royalty-free patent license under Licensed Patents to make, use, sell,
offer to sell, import and otherwise transfer the Contribution of such
Contributor, if any, in source code and object code form. This patent
license shall apply to the combination of the Contribution and the
Program if, at the time the Contribution is added by the Contributor,
such addition of the Contribution causes such combination to be covered
by the Licensed Patents. The patent license shall not apply to any other
combinations which include the Contribution. No hardware per se is
licensed hereunder.
c) Recipient understands that although each Contributor
grants the licenses to its Contributions set forth herein, no assurances
are provided by any Contributor that the Program does not infringe the
patent or other intellectual property rights of any other entity. Each
Contributor disclaims any liability to Recipient for claims brought by
any other entity based on infringement of intellectual property rights
or otherwise. As a condition to exercising the rights and licenses
granted hereunder, each Recipient hereby assumes sole responsibility to
secure any other intellectual property rights needed, if any. For
example, if a third party patent license is required to allow Recipient
to distribute the Program, it is Recipient's responsibility to acquire
that license before distributing the Program.
d) Each Contributor represents that to its knowledge it
has sufficient copyright rights in its Contribution, if any, to grant
the copyright license set forth in this Agreement.
3. REQUIREMENTS
A Contributor may choose to distribute the Program in object code
form under its own license agreement, provided that:
a) it complies with the terms and conditions of this
Agreement; and
b) its license agreement:
i) effectively disclaims on behalf of all Contributors
all warranties and conditions, express and implied, including warranties
or conditions of title and non-infringement, and implied warranties or
conditions of merchantability and fitness for a particular purpose;
ii) effectively excludes on behalf of all Contributors
all liability for damages, including direct, indirect, special,
incidental and consequential damages, such as lost profits;
iii) states that any provisions which differ from this
Agreement are offered by that Contributor alone and not by any other
party; and
iv) states that source code for the Program is available
from such Contributor, and informs licensees how to obtain it in a
reasonable manner on or through a medium customarily used for software
exchange.
When the Program is made available in source code form:
a) it must be made available under this Agreement; and
b) a copy of this Agreement must be included with each
copy of the Program.
Contributors may not remove or alter any copyright notices contained
within the Program.
Each Contributor must identify itself as the originator of its
Contribution, if any, in a manner that reasonably allows subsequent
Recipients to identify the originator of the Contribution.
4. COMMERCIAL DISTRIBUTION
Commercial distributors of software may accept certain
responsibilities with respect to end users, business partners and the
like. While this license is intended to facilitate the commercial use of
the Program, the Contributor who includes the Program in a commercial
product offering should do so in a manner which does not create
potential liability for other Contributors. Therefore, if a Contributor
includes the Program in a commercial product offering, such Contributor
("Commercial Contributor") hereby agrees to defend and
indemnify every other Contributor ("Indemnified Contributor")
against any losses, damages and costs (collectively "Losses")
arising from claims, lawsuits and other legal actions brought by a third
party against the Indemnified Contributor to the extent caused by the
acts or omissions of such Commercial Contributor in connection with its
distribution of the Program in a commercial product offering. The
obligations in this section do not apply to any claims or Losses
relating to any actual or alleged intellectual property infringement. In
order to qualify, an Indemnified Contributor must: a) promptly notify
the Commercial Contributor in writing of such claim, and b) allow the
Commercial Contributor to control, and cooperate with the Commercial
Contributor in, the defense and any related settlement negotiations. The
Indemnified Contributor may participate in any such claim at its own
expense.
For example, a Contributor might include the Program in a commercial
product offering, Product X. That Contributor is then a Commercial
Contributor. If that Commercial Contributor then makes performance
claims, or offers warranties related to Product X, those performance
claims and warranties are such Commercial Contributor's responsibility
alone. Under this section, the Commercial Contributor would have to
defend claims against the other Contributors related to those
performance claims and warranties, and if a court requires any other
Contributor to pay any damages as a result, the Commercial Contributor
must pay those damages.
5. NO WARRANTY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
PROVIDED 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. Each Recipient is solely
responsible for determining the appropriateness of using and
distributing the Program and assumes all risks associated with its
exercise of rights under this Agreement , including but not limited to
the risks and costs of program errors, compliance with applicable laws,
damage to or loss of data, programs or equipment, and unavailability or
interruption of operations.
6. DISCLAIMER OF LIABILITY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT
NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
WITHOUT LIMITATION LOST PROFITS), 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 OR
DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
7. GENERAL
If any provision of this Agreement is invalid or unenforceable under
applicable law, it shall not affect the validity or enforceability of
the remainder of the terms of this Agreement, and without further action
by the parties hereto, such provision shall be reformed to the minimum
extent necessary to make such provision valid and enforceable.
If Recipient institutes patent litigation against any entity
(including a cross-claim or counterclaim in a lawsuit) alleging that the
Program itself (excluding combinations of the Program with other
software or hardware) infringes such Recipient's patent(s), then such
Recipient's rights granted under Section 2(b) shall terminate as of the
date such litigation is filed.
All Recipient's rights under this Agreement shall terminate if it
fails to comply with any of the material terms or conditions of this
Agreement and does not cure such failure in a reasonable period of time
after becoming aware of such noncompliance. If all Recipient's rights
under this Agreement terminate, Recipient agrees to cease use and
distribution of the Program as soon as reasonably practicable. However,
Recipient's obligations under this Agreement and any licenses granted by
Recipient relating to the Program shall continue and survive.
Everyone is permitted to copy and distribute copies of this
Agreement, but in order to avoid inconsistency the Agreement is
copyrighted and may only be modified in the following manner. The
Agreement Steward reserves the right to publish new versions (including
revisions) of this Agreement from time to time. No one other than the
Agreement Steward has the right to modify this Agreement. The Eclipse
Foundation is the initial Agreement Steward. The Eclipse Foundation may
assign the responsibility to serve as the Agreement Steward to a
suitable separate entity. Each new version of the Agreement will be
given a distinguishing version number. The Program (including
Contributions) may always be distributed subject to the version of the
Agreement under which it was received. In addition, after a new version
of the Agreement is published, Contributor may elect to distribute the
Program (including its Contributions) under the new version. Except as
expressly stated in Sections 2(a) and 2(b) above, Recipient receives no
rights or licenses to the intellectual property of any Contributor under
this Agreement, whether expressly, by implication, estoppel or
otherwise. All rights in the Program not expressly granted under this
Agreement are reserved.
This Agreement is governed by the laws of the State of New York and
the intellectual property laws of the United States of America. No party
to this Agreement will bring a legal action under this Agreement more
than one year after the cause of action arose. Each party waives its
rights to a jury trial in any resulting litigation.
================================================
FILE: pom.xml
================================================
4.0.0io.github.clojuretools.build0.10.14-SNAPSHOTtools.buildClojure builds as Clojure programs.org.clojurepom.contrib1.4.0puredangerAlex Millertrue1.12.4org.clojureclojure${clojure.version}org.clojuretools.deps0.29.1598org.clojuretools.namespace1.5.1org.slf4jslf4j-nop1.7.36sonatype-nexus-staginghttps://s01.oss.sonatype.org/content/repositories/snapshotssrc/main/resourcestrueorg.apache.maven.pluginsmaven-resources-plugin3.4.0com.theoryinpractiseclojure-maven-plugin1.7.1true${clojure.warnOnReflection}trueclojure-compilenoneclojure-testtesttestorg.sonatype.pluginsnexus-staging-maven-plugin1.7.0truesonatype-nexus-staginghttps://s01.oss.sonatype.org/truescm:git:git@github.com:clojure/tools.build.gitscm:git:git@github.com:clojure/tools.build.gitgit@github.com:clojure/tools.build.gitHEAD
================================================
FILE: src/main/clojure/clojure/tools/build/api/specs.clj
================================================
(ns clojure.tools.build.api.specs
(:require [clojure.spec.alpha :as s]))
(s/def ::lib qualified-ident?)
(s/def ::path string?)
(s/def ::paths (s/coll-of string?))
;; there are better specs in clojure.tools.deps.specs, but no basis spec yet
;; just doing a simple check here
(s/def ::basis (s/nilable map?))
================================================
FILE: src/main/clojure/clojure/tools/build/api.clj
================================================
(ns clojure.tools.build.api
(:require
[clojure.java.io :as jio]
[clojure.set :as set]
[clojure.spec.alpha :as s]
[clojure.string :as str]
[clojure.tools.build.util.file :as file]
[clojure.tools.build.api.specs :as specs])
(:import
[java.io File]))
(set! *warn-on-reflection* true)
;; Project root dir, defaults to current directory
(def ^:dynamic *project-root*
"Project root path, defaults to current directory.
Use `resolve-path` to resolve relative paths in terms of the *project-root*.
Use `set-project-root!` to override the default for all tasks."
".")
(defn set-project-root!
"Set *project-root* dir (default is \".\")"
[root]
(alter-var-root #'*project-root* (constantly root)))
(defmacro with-project-root
"Execute forms in a bound project path (string) other than the default (\".\")"
[path & forms]
`(binding [*project-root* ~path] ~@forms))
(defn resolve-path
"If path is absolute or root-path is nil then return path,
otherwise resolve relative to *project-root*."
^File [path]
(let [path-file (jio/file path)]
(if (.isAbsolute path-file)
;; absolute, ignore root
path-file
;; relative to *project-root*
(jio/file *project-root* path-file))))
(defn- assert-required
"Check that each key in required coll is a key in params and throw if
required are missing in params, otherwise return nil."
[task params required]
(let [missing (set/difference (set required) (set (keys params)))]
(when (seq missing)
(throw (ex-info (format "Missing required params for %s: %s" task (vec (sort missing))) (or params {}))))))
(defn- assert-specs
"Check that key in params satisfies the spec. Throw if it exists and
does not conform to the spec, otherwise return nil."
[task params & key-specs]
(doseq [[key spec] (partition-all 2 key-specs)]
(let [val (get params key)]
(when (and val (not (s/valid? spec val)))
(throw (ex-info (format "Invalid param %s in call to %s: got %s, expected %s" key task (pr-str val) (s/form spec)) {}))))))
;; File tasks
(defn delete
"Delete file or directory recursively, if it exists. Returns nil.
Options:
:path - required, path to file or directory"
[{:keys [path] :as params}]
(assert-required "delete" params [:path])
(assert-specs "delete" params :path ::specs/path)
(let [root-file (resolve-path path)]
;(println "root-file" root-file)
(when (.exists root-file)
(file/delete root-file))))
(defn copy-file
"Copy one file from source to target, creating target dirs if needed.
Returns nil.
Options:
:src - required, source path
:target - required, target path"
[{:keys [src target] :as params}]
(assert-required "copy-file" params [:src :target])
(assert-specs "copy-file" params
:src ::specs/path
:target ::specs/path)
(file/copy-file (resolve-path src) (resolve-path target)))
(defn write-file
"Writes a file at path, will create parent dirs if needed. Returns nil.
File contents may be specified either with :content (for data, that
will be pr-str'ed) or with :string for the string to write. If
neither is specified, an empty file is created (like touch).
Options:
:path - required, file path
:content - val to write, will pr-str
:string - string to write
:opts - coll of writer opts like :append and :encoding (per clojure.java.io)"
[{:keys [path content string opts] :as params}]
(assert-required "write-file" params [:path])
(assert-specs "write-file" params :path ::specs/path)
(let [f (resolve-path path)]
(cond
content (apply file/ensure-file f (pr-str content) opts)
string (apply file/ensure-file f string opts)
:else (file/ensure-file f))))
(defn copy-dir
"Copy the contents of the src-dirs to the target-dir, optionally do text replacement.
Returns nil.
Globs are wildcard patterns for specifying sets of files in a directory
tree, as specified in the glob syntax of java.nio.file.FileSystem:
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/file/FileSystem.html#getPathMatcher(java.lang.String)
Options:
:target-dir - required, dir to write files, will be created if it doesn't exist
:src-dirs - required, coll of dirs to copy from
:include - glob of files to include, default = \"**\"
:ignores - collection of ignore regex patterns (applied only to file names),
see clojure.tools.build.tasks.copy/default-ignores for defaults
:replace - map of source to replacement string in files
:non-replaced-exts - coll of extensions to skip when replacing (still copied)
default = [\"jpg\" \"jpeg\" \"png\" \"gif\" \"bmp\"]"
[params]
(assert-required "copy" params [:target-dir :src-dirs])
(assert-specs "copy" params
:target-dir ::specs/path
:src-dirs ::specs/paths)
((requiring-resolve 'clojure.tools.build.tasks.copy/copy) params))
;; Basis tasks
(defn create-basis
"Create a basis from a set of deps sources and a set of aliases. By default, use
root, user, and project deps and no aliases (essentially the same classpath you
get by default from the Clojure CLI).
Each dep source value can be :standard, a string path, a deps edn map, or nil.
Sources are merged in the order - :root, :user, :project, :extra.
Options (note, paths resolved via *project-root*):
:dir - directory root path, defaults to current directory
:root - dep source, default = :standard
:user - dep source, default = nil (for reproducibility, not included)
:project - dep source, default = :standard (\"./deps.edn\")
:extra - dep source, default = nil
:aliases - coll of aliases of argmaps to apply to subprocesses
Returns a runtime basis, which is the initial merged deps edn map plus these keys:
:resolve-args - the resolve args passed in, if any
:classpath-args - the classpath args passed in, if any
:libs - lib map, per resolve-deps
:classpath - classpath map per make-classpath-map
:classpath-roots - vector of paths in classpath order"
([]
((requiring-resolve 'clojure.tools.build.tasks.create-basis/create-basis)))
([params]
((requiring-resolve 'clojure.tools.build.tasks.create-basis/create-basis) params)))
;; Process tasks
(defn java-command
"Create Java command line args. The classpath will be the combination of
:cp followed by the classpath from the basis, both are optional.
Note that 'java-command' will NOT resolve any relative paths from basis
or cp in terms of *project-root*, you will get a classpath with the same
relative paths. 'process' (if run with this output), will run in the
context of the *project-root* directory.
Options:
:java-cmd - Java command, default = $JAVA_CMD or 'java' on $PATH, or $JAVA_HOME/bin/java
:cp - coll of string classpath entries, used first (if provided)
:basis - runtime basis used for classpath and jvm opts from aliases, used last (if provided)
:java-opts - coll of string jvm opts
:main - required, main class symbol
:main-args - coll of main class args
:use-cp-file - one of:
:auto (default) - use only if os=windows && Java >= 9 && command length >= 8k
:always - always write classpath to temp file and include
:never - never write classpath to temp file (pass on command line)
Returns map suitable for passing to 'process' with keys:
:command-args - coll of command arg strings"
[params]
(assert-required "java-command" params [:main])
((requiring-resolve 'clojure.tools.build.tasks.process/java-command) params))
(defn process
"Exec the command made from command-args, redirect out and err as directed,
and return {:exit exit-code, :out captured-out, :err captured-err}.
Options:
:command-args - required, coll of string args
:dir - directory to run the command from, default *project-root*
:out - one of :inherit :capture :write :append :ignore
:err - one of :inherit :capture :write :append :ignore
:out-file - file path to write if :out is :write or :append
:err-file - file path to write if :err is :write or :append
:env - map of environment variables to set
The :out and :err input flags take one of the following options:
:inherit - inherit the stream and write the subprocess io to this process's stream (default)
:capture - capture the stream to a string and return it
:write - write to :out-file or :err-file
:append - append to :out-file or :err-file
:ignore - ignore the stream"
[params]
(assert-required "process" params [:command-args])
(assert-specs "process" params
:dir ::specs/path
:out-file ::specs/path
:err-file ::specs/path)
((requiring-resolve 'clojure.tools.build.tasks.process/process) params))
;; Git tasks
(defn git-process
"Run git process in the specified dir using git-command with git-args (which should not
start with \"git\"). git-args may either be a string (split on whitespace) or a vector
of strings. By default, stdout is captured, trimmed, and returned.
Options:
:dir - dir to invoke this command from, default = current directory
:git-command - git command to use, default = \"git\"
:git-args - required, coll of git-arg strings forming a command line OR
a string (do not use if args may have embedded spaces)
:capture - :out (default) or :err, else nothing
Examples:
(api/git-process {:git-args \"rev-list HEAD --count\"})
(api/git-process {:git-args \"branch --show-current\"})
(api/git-process {:git-args \"rev-parse --short HEAD\"})
(api/git-process {:git-args \"push\", :capture nil})"
[params]
(assert-required "git-command" params [:git-args])
(assert-specs "git-process" params
:dir ::specs/path
:git-command (s/nilable string?)
:git-args (s/or :args (s/coll-of string?) :line string?)
:capture (s/nilable keyword?))
(let [{:keys [dir git-command git-args capture] :or {git-command "git", capture :out}} params
git-args (vec
(concat [git-command]
(if (string? git-args)
(str/split git-args #"\s")
git-args)))
proc-params (cond-> {:command-args git-args}
capture (assoc capture :capture)
dir (assoc :dir (.getPath (resolve-path dir))))
output (process proc-params)]
(when capture
(some-> output capture str/trim))))
(defn git-count-revs
"Shells out to git and returns count of commits on this branch:
git rev-list HEAD --count
Options:
:dir - dir to invoke this command from, default = current directory
:git-command - git command to use, default = \"git\"
:path - path to count commits for relative to dir"
[{:keys [dir git-command path] :or {git-command "git"} :as params}]
(assert-specs "git-count-revs" params
:dir ::specs/path
:git-command (s/nilable string?)
:path ::specs/path)
(git-process
(cond-> {:git-args (cond-> ["rev-list" "HEAD" "--count"]
path (conj "--" path))}
dir (assoc :dir dir)
git-command (assoc :git-command git-command))))
;; Compile tasks
(defn compile-clj
"Compile Clojure source to classes in :class-dir.
Clojure source files are found in :basis :paths by default, or override with :src-dirs.
Namespaces and order of compilation are one of:
* :ns-compile - compile these namespaces, in this order
* :sort - find all namespaces in source files and use either :topo (default)
or :bfs to order them for compilation
Options:
:basis - required, basis to use when compiling
:class-dir - required, dir to write classes, will be created if needed
:src-dirs - coll of Clojure source dirs, used to find all Clojure nses to compile
:ns-compile - coll of specific namespace symbols to compile
:sort - :topo (default) or :bfs for breadth-first search
:compile-opts - map of Clojure compiler options:
{:disable-locals-clearing false
:elide-meta [:doc :file :line ...]
:direct-linking false}
:bindings - map of Var to value to be set during compilation, for example:
{#'clojure.core/*assert* false
#'clojure.core/*warn-on-reflection* true}
:filter-nses - coll of symbols representing a namespace prefix to include
Additional options flow to the forked process doing the compile:
:java-cmd - Java command, default = $JAVA_CMD or 'java' on $PATH, or $JAVA_HOME/bin/java
:java-opts - coll of string jvm opts
:use-cp-file - one of:
:auto (default) - use only if os=windows && Java >= 9 && command length >= 8k
:always - always write classpath to temp file and include
:never - never write classpath to temp file (pass on command line)
:out - one of :inherit :capture :write :append :ignore
:err - one of :inherit :capture :write :append :ignore
:out-file - file path to write if :out is :write or :append
:err-file - file path to write if :err is :write or :append
Returns nil, or if needed a map with keys:
:out captured-out
:err captured-err"
[params]
(assert-required "compile-clj" params [:basis :class-dir])
(assert-specs "compile-clj" params
:class-dir ::specs/path
:src-dirs ::specs/paths
:compile-opts map?
:bindings map?
:basis ::specs/basis)
((requiring-resolve 'clojure.tools.build.tasks.compile-clj/compile-clj) params))
(defn javac
"Compile Java source to classes. Returns nil.
Options:
:src-dirs - required, coll of Java source dirs
:class-dir - required, dir to write classes, will be created if needed
:basis - classpath basis to use when compiling
:javac-opts - coll of string opts, like [\"-source\" \"8\" \"-target\" \"8\"]"
[params]
(assert-required "javac" params [:src-dirs :class-dir])
(assert-specs "javac" params
:src-dirs ::specs/paths
:class-dir ::specs/path)
((requiring-resolve 'clojure.tools.build.tasks.javac/javac) params))
;; Jar/zip tasks
(defn pom-path
"Calculate path to pom.xml in jar meta (same path used by write-pom).
Relative path in jar is:
META-INF/maven///pom.xml
If :class-dir provided, return path will start with resolved class-dir
(which may be either absolute or relative), otherwise just relative
path in jar.
Options:
:lib - required, used to form the relative path in jar to pom.xml
:class-dir - optional, if provided will be resolved and form the root of the path"
[params]
(assert-required "pom-path" params [:lib])
(assert-specs "pom-path" params
:lib ::specs/lib
:class-dir ::specs/path)
(let [{:keys [class-dir lib]} params
pom-dir ((requiring-resolve 'clojure.tools.build.tasks.write-pom/meta-maven-path) {:lib lib})
pom-file (if class-dir
(jio/file (resolve-path class-dir) pom-dir "pom.xml")
(jio/file pom-dir "pom.xml"))]
(.toString pom-file)))
(defn write-pom
"Write pom.xml and pom.properties files to the class dir under
META-INF/maven/group-id/artifact-id/ (where Maven typically writes
these files), or to target (exactly one of :class-dir and :target must
be provided).
Optionally use :src-pom to provide a pom template (or a default will
be generated from the provided attributes). The pom deps, dirs, and
repos from the basis will replace those sections of the template. Note
that the :src-pom template is not validated and should contain required
elements such as modelVersion.
If a repos map is provided it supersedes the repos in the basis.
Returns nil.
Options:
:basis - required, used to pull deps, repos
:src-pom - source pom.xml to synchronize from, default = \"./pom.xml\"
may be :none to ignore any existing pom.xml file
:class-dir - root dir for writing pom files, created if needed
:target - file path to write pom if no :class-dir specified
:lib - required, project lib symbol
:version - required, project version
:scm - map of scm properties to write in pom
keys: :connection, :developerConnection, :tag, :url
See: https://maven.apache.org/pom.html#SCM for details
:src-dirs - coll of src dirs
:resource-dirs - coll of resource dirs
:repos - map of repo name to repo config, replaces repos from deps.edn
:pom-data - vector of hiccup-style extra pom top elements to include when
:src-pom is :none or the source pom.xml does not exist:
[[:licenses
[:license
[:name \"Eclipse Public License 1.0\"]
[:url \"https://opensource.org/license/epl-1-0/\"]
[:distribution \"repo\"]]]
[:organization \"Super Corp\"]]
The pom-data MUST NOT include:
:modelVersion, :packaging, :groupId, :artifactId, :version, :name,
:deps, :repositories, :build, or :scm"
[params]
(assert-required "write-pom" params [:basis :lib :version])
(assert-specs "write-pom" params
:src-pom (s/or :none #{:none} :path ::specs/path)
:class-dir ::specs/path
:target ::specs/path
:lib ::specs/lib
:version string?
:scm map?
:src-dirs ::specs/paths
:resource-dirs ::specs/paths
:pom-data vector?)
((requiring-resolve 'clojure.tools.build.tasks.write-pom/write-pom) params))
(defn jar
"Create jar file containing contents of class-dir. Use main in the manifest
if provided. Returns nil.
Options:
:class-dir - required, dir to include in jar
:jar-file - required, jar to write
:main - main class symbol
:manifest - map of manifest attributes, merged last over defaults+:main"
[params]
(assert-required "jar" params [:class-dir :jar-file])
(assert-specs "jar" params
:class-dir ::specs/path
:jar-file ::specs/path)
((requiring-resolve 'clojure.tools.build.tasks.jar/jar) params))
(defn uber
"Create uberjar file. An uberjar is a self-contained jar file containing
both the project contents AND the contents of all dependencies.
The project contents are represented by the class-dir. Use other tasks to
put Clojure source, class files, a pom file, or other resources in the
class-dir. In particular, see the copy-dir, write-pom, compile-clj, and
javac tasks.
The dependencies are pulled from the basis. All transitive deps will be
included. Dependency jars are expanded for inclusion in the uberjar.
Use :exclude to exclude specific paths from the expanded deps. Use
conflict-handlers to handle conflicts that may occur if two dependency
jar files include a file at the same path. See below for more detail.
If a main class or manifest are provided, those are put in the uberjar
META-INF/MANIFEST.MF file. Providing a main allows the jar to be
invoked with java -jar.
Returns nil.
Options:
:uber-file - required, uber jar file to create
:class-dir - required, local class dir to include
:basis - used to pull dep jars
:main - main class symbol
:manifest - map of manifest attributes, merged last over defaults + :main
:exclude - coll of string patterns (regex) to exclude from deps
:conflict-handlers - map of string pattern (regex) to built-in handlers,
symbols to eval, or function instances
When combining jar files into an uber jar, multiple jars may contain a file
at the same path. The conflict handlers are a map of string regex pattern
to:
a keyword (to use a built-in handler) or
a symbol (to resolve and invoke) or
a function instance
The special key `:default` specifies the default behavior if not matched.
Conflict handler signature (fn [params]) => effect-map:
params:
:path - String, path in uber jar, matched by regex
:in - InputStream to incoming file (see stream->string if needed)
:existing - File, existing File at path
:lib - symbol, lib source for incoming conflict
:state - map, available for retaining state during uberjar process
Handler should return effect-map with optional keys:
:state - updated state map
:write - map of string path to map of :string (string) or
:stream (InputStream) to write and optional :append
flag. Omit if no files to write.
Available built-in conflict handlers:
:ignore - don't do anything (default)
:overwrite - overwrite (replaces prior file)
:append - append the file with a blank line separator
:append-dedupe - append the file but dedupe appended sections
:data-readers - merge data_readers.clj
:warn - print a warning
:error - throw an error
Default conflict handlers map:
{\"^data_readers.clj[c]?$\" :data-readers
\"^META-INF/services/.*\" :append
\"(?i)^(META-INF/)?(COPYRIGHT|NOTICE|LICENSE)(\\\\.(txt|md))?$\" :append-dedupe
:default :ignore}"
[params]
(assert-required "uber" params [:class-dir :uber-file])
(assert-specs "uber" params
:class-dir ::specs/path
:uber-file ::specs/path)
((requiring-resolve 'clojure.tools.build.tasks.uber/uber) params))
(defn zip
"Create zip file containing contents of src dirs. Returns nil.
Options:
:src-dirs - required, coll of source directories to include in zip
:zip-file - required, zip file to create"
[params]
(assert-required "zip" params [:src-dirs :zip-file])
(assert-specs "zip" params
:src-dirs ::specs/paths
:zip-file ::specs/path)
((requiring-resolve 'clojure.tools.build.tasks.zip/zip) params))
(defn unzip
"Unzip zip file to target-dir. Returns nil.
Options:
:zip-file - required, zip file to unzip
:target-dir - required, directory to unzip in"
[params]
(assert-required "unzip" params [:zip-file :target-dir])
(assert-specs "unzip" params
:zip-file ::specs/path
:target-dir ::specs/path)
((requiring-resolve 'clojure.tools.build.tasks.zip/unzip) params))
;; Maven tasks
(defn install
"Install pom and jar to local Maven repo.
Returns nil.
Options:
:basis - required, used for :mvn/local-repo
:lib - required, lib symbol
:classifier - classifier string, if needed
:version - required, string version
:jar-file - required, path to jar file
:class-dir - required, used to find the pom file"
[params]
(assert-required "install" params [:basis :lib :version :jar-file :class-dir])
(assert-specs "install" params
:lib ::specs/lib
:jar-file ::specs/path
:class-dir ::specs/path)
((requiring-resolve 'clojure.tools.build.tasks.install/install) params))
================================================
FILE: src/main/clojure/clojure/tools/build/tasks/compile_clj.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.tools.build.tasks.compile-clj
(:require
[clojure.java.io :as jio]
[clojure.pprint :as pprint]
[clojure.string :as str]
[clojure.tools.build.api :as api]
[clojure.tools.build.util.file :as file]
[clojure.tools.build.tasks.process :as process]
[clojure.tools.namespace.find :as find]
[clojure.tools.namespace.dependency :as dependency]
[clojure.tools.namespace.parse :as parse])
(:import
[java.io File]
[java.nio.file Files]
[java.nio.file.attribute FileAttribute]))
(set! *warn-on-reflection* true)
(defn- write-compile-script!
^File [^File script-file ^File compile-dir nses compiler-opts bindings]
(let [compile-bindings (merge bindings
{#'*compile-path* (.toString compile-dir)
#'*compiler-options* compiler-opts})
binding-nses (->> compile-bindings
keys
(map #(-> % symbol namespace symbol)) ;; Var->namespace
distinct
(remove #(= % 'clojure.core)))
requires (map (fn [n] `(require '~n)) binding-nses)
do-compile `(with-bindings ~compile-bindings
~@(map (fn [n] `(~'compile '~n)) nses)
(System/exit 0))
script (->> (conj (vec requires) do-compile)
(map #(with-out-str (pprint/pprint %)))
(str/join (System/lineSeparator)))]
(spit script-file script)))
(defn- ns->path
[ns-sym]
(str/replace (clojure.lang.Compiler/munge (str ns-sym)) \. \/))
(defn- nses-in-bfs
[dirs]
(mapcat #(find/find-namespaces-in-dir % find/clj) dirs))
(defn- nses-in-topo
[dirs]
(let [ns-decls (mapcat find/find-ns-decls-in-dir dirs)
ns-candidates (set (map parse/name-from-ns-decl ns-decls))
graph (reduce
(fn [graph decl]
(let [sym (parse/name-from-ns-decl decl)]
(reduce
(fn [graph dep] (dependency/depend graph sym dep))
graph
(parse/deps-from-ns-decl decl))))
(dependency/graph)
ns-decls)]
(->> graph
dependency/topo-sort
(filter ns-candidates) ;; only keep stuff in these dirs
(#(concat % ns-candidates)) ;; but make sure everything is in there at least once
distinct)))
(defn- basis-paths
"Extract all path entries from basis, in classpath order"
[{:keys [classpath classpath-roots]}]
(let [path-set (->> classpath
(filter #(contains? (val %) :path-key))
(map key)
set)]
(filter path-set classpath-roots)))
(defn compile-clj
[{:keys [basis src-dirs compile-opts ns-compile filter-nses class-dir sort bindings] :as params
:or {sort :topo}}]
(let [working-dir (.toFile (Files/createTempDirectory "compile-clj" (into-array FileAttribute [])))
compile-dir-file (file/ensure-dir (api/resolve-path class-dir))
clj-paths (map api/resolve-path (or src-dirs (basis-paths basis)))
nses (cond
(seq ns-compile) ns-compile
(= sort :topo) (nses-in-topo clj-paths)
(= sort :bfs) (nses-in-bfs clj-paths)
:else (throw (ex-info "Missing :ns-compile or :sort order in compile-clj task" {})))
working-compile-dir (file/ensure-dir (jio/file working-dir "compile-clj"))
compile-script (jio/file working-dir "compile.clj")
_ (write-compile-script! compile-script working-compile-dir nses compile-opts bindings)
;; java-command will run in context of *project-dir* - basis, classpaths, etc
;; should all be relative to that (or absolute like working-compile-dir)
process-args (merge
(process/java-command
(merge
(select-keys params [:java-cmd :java-opts :use-cp-file])
{:cp [(.getPath working-compile-dir) class-dir]
:basis basis
:main 'clojure.main
:main-args [(.getCanonicalPath compile-script)]}))
(select-keys params [:out :err :out-file :err-file]))
_ (spit (jio/file working-dir "compile.args") (str/join " " (:command-args process-args)))
{exit :exit, ps-out :out, ps-err :err} (process/process process-args)
ret (cond-> nil
ps-out (assoc :out ps-out)
ps-err (assoc :err ps-err))]
(if (zero? exit)
(do
(if (seq filter-nses)
(file/copy-contents working-compile-dir compile-dir-file (map ns->path filter-nses))
(file/copy-contents working-compile-dir compile-dir-file))
;; only delete on success, otherwise leave the evidence!
(file/delete working-dir)
ret)
(throw (ex-info (str "Clojure compilation failed, working dir preserved: " (.toString working-dir)) ret)))))
================================================
FILE: src/main/clojure/clojure/tools/build/tasks/copy.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.tools.build.tasks.copy
(:require
[clojure.string :as str]
[clojure.tools.build.api :as api]
[clojure.tools.build.util.file :as file])
(:import
[java.io File]
[java.nio.file FileSystems FileVisitor FileVisitResult Files Path LinkOption]))
(set! *warn-on-reflection* true)
;; copy spec:
;; :from (coll of dirs), default = ["."]
;; ;include (glob), default = "**"
;; :replace (map of replacements) - performed while copying
(defn- match-paths
"Match glob to paths under root and return a collection of Path objects"
[^File root glob]
(let [root-path (.toPath root)
matcher (.getPathMatcher (FileSystems/getDefault) (str "glob:" glob))
paths (volatile! [])
visitor (reify FileVisitor
(visitFile [_ path _attrs]
(when (.matches matcher (.relativize root-path ^Path path))
(vswap! paths conj path))
FileVisitResult/CONTINUE)
(visitFileFailed [_ _path _ex] FileVisitResult/CONTINUE)
(preVisitDirectory [_ _ _] FileVisitResult/CONTINUE)
(postVisitDirectory [_ _ _] FileVisitResult/CONTINUE))]
(Files/walkFileTree root-path visitor)
@paths))
(def default-ignores
[".*~$"
"^#.*#$"
"^\\.#.*"
"^.DS_Store$"])
(defn ignore? [name ignore-regexes]
(boolean (some #(re-matches % name) ignore-regexes)))
(def default-non-replaced-exts
["jpg" "jpeg" "png" "gif" "bmp"])
(defn- ends-with-ext?
[exts path]
(loop [[ext & es] exts]
(if ext
(if (str/ends-with? path ext)
true
(recur es))
false)))
(defn copy
[{:keys [target-dir src-dirs include replace ignores non-replaced-exts]
:or {include "**", ignores default-ignores, non-replaced-exts default-non-replaced-exts}
:as _params}]
(let [to-path (.toPath (file/ensure-dir (api/resolve-path target-dir)))
ignore-regexes (map re-pattern ignores)
non-replaced (map #(str "." %) default-non-replaced-exts)]
(doseq [dir src-dirs]
;(println "from" dir)
(let [from-file (api/resolve-path dir)
paths (match-paths from-file include)]
(doseq [^Path path paths]
(let [path-file (.toFile path)
path-file-name (.getName path-file)
target-file (.toFile (.resolve to-path (.relativize (.toPath from-file) path)))]
(when-not (ignore? path-file-name ignore-regexes)
;(println "copying" (.toString path-file) (.toString target-file) (boolean (not (empty? replace))))
(if (or (empty? replace) (ends-with-ext? non-replaced (.getName path-file)))
(file/copy-file path-file target-file)
(let [contents (slurp path-file)
replaced (reduce (fn [s [find replace]] (str/replace s find replace))
contents replace)
perms (when (contains? (.supportedFileAttributeViews (FileSystems/getDefault)) "posix")
(Files/getPosixFilePermissions (.toPath path-file)
(into-array LinkOption [LinkOption/NOFOLLOW_LINKS])))]
(file/ensure-file target-file replaced :append false)
(when perms
(Files/setPosixFilePermissions (.toPath target-file) perms)
nil))))))))))
================================================
FILE: src/main/clojure/clojure/tools/build/tasks/create_basis.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.tools.build.tasks.create-basis
(:require
[clojure.java.io :as jio]
[clojure.tools.build.api :as api]
[clojure.tools.deps :as deps]
[clojure.tools.deps.util.dir :as dir]))
(defn create-basis
"Wrapper for deps/create-basis, but ensure relative paths are resolved
relative to *project-root*.
Options (note, paths resolved via *project-root*):
:root - dep source, default = :standard
:user - dep source, default = nil
:project - dep source, default = :standard (\"./deps.edn\")
:extra - dep source, default = nil
:aliases - coll of aliases of argmaps to apply to subprocesses"
([]
(create-basis nil))
([params]
(dir/with-dir (jio/file api/*project-root*)
(deps/create-basis (merge {:user nil} params)))))
================================================
FILE: src/main/clojure/clojure/tools/build/tasks/install.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.tools.build.tasks.install
(:require
[clojure.java.io :as jio]
[clojure.tools.deps.util.maven :as mvn]
[clojure.tools.build.api :as api])
(:import
[org.eclipse.aether.artifact DefaultArtifact]
[org.eclipse.aether.installation InstallRequest]))
(set! *warn-on-reflection* true)
(defn install
[{:keys [basis lib classifier version jar-file class-dir] :as params}]
(let [{:mvn/keys [local-repo]} basis
group-id (namespace lib)
artifact-id (name lib)
jar-file-file (api/resolve-path jar-file)
pom-dir (jio/file (api/resolve-path class-dir) "META-INF" "maven" group-id artifact-id)
pom (jio/file pom-dir "pom.xml")
system (mvn/make-system)
session (mvn/make-session system (or local-repo @mvn/cached-local-repo))
jar-artifact (.setFile (DefaultArtifact. group-id artifact-id classifier "jar" version) jar-file-file)
artifacts (cond-> [jar-artifact]
(and pom-dir (.exists pom)) (conj (.setFile (DefaultArtifact. group-id artifact-id classifier "pom" version) pom)))
install-request (.setArtifacts (InstallRequest.) artifacts)]
(.install system session install-request)
nil))
================================================
FILE: src/main/clojure/clojure/tools/build/tasks/jar.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.tools.build.tasks.jar
(:require
[clojure.java.io :as jio]
[clojure.tools.build.api :as api]
[clojure.tools.build.util.file :as file]
[clojure.tools.build.util.zip :as zip]
[clojure.string :as str])
(:import
[java.util.jar Manifest JarOutputStream]))
(defn jar
[{mf-attrs :manifest, :keys [class-dir jar-file main] :as params}]
(let [jar-file (api/resolve-path jar-file)
class-dir-file (file/ensure-dir (api/resolve-path class-dir))
mf-attr-strs (reduce-kv (fn [m k v] (assoc m (str k) (str v))) nil mf-attrs)]
(file/ensure-dir (.getParent jar-file))
(let [manifest (Manifest.)]
(zip/fill-manifest! manifest
(merge
(cond->
{"Manifest-Version" "1.0"
"Created-By" "org.clojure/tools.build"
"Build-Jdk-Spec" (System/getProperty "java.specification.version")}
main (assoc "Main-Class" (str/replace (str main) \- \_)))
mf-attr-strs))
(with-open [jos (JarOutputStream. (jio/output-stream jar-file) manifest)]
(zip/copy-to-zip jos class-dir-file)))))
================================================
FILE: src/main/clojure/clojure/tools/build/tasks/javac.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.tools.build.tasks.javac
(:require
[clojure.string :as str]
[clojure.tools.build.api :as api]
[clojure.tools.build.util.file :as file])
(:import
[java.io File]
[javax.tools ToolProvider DiagnosticListener]))
(set! *warn-on-reflection* true)
(defn javac
[{:keys [basis javac-opts class-dir src-dirs] :as params}]
(let [{:keys [libs]} basis]
(when (seq src-dirs)
(let [class-dir (file/ensure-dir (api/resolve-path class-dir))
compiler (ToolProvider/getSystemJavaCompiler)
listener (reify DiagnosticListener (report [_ diag] (println (str diag))))
file-mgr (.getStandardFileManager compiler listener nil nil)
class-dir-path (.getPath class-dir)
classpath (str/join File/pathSeparator (conj (mapcat :paths (vals libs)) class-dir-path))
options (concat ["-classpath" classpath "-d" class-dir-path] javac-opts)
java-files (mapcat #(file/collect-files (api/resolve-path %) :collect (file/suffixes ".java")) src-dirs)
file-objs (.getJavaFileObjectsFromFiles file-mgr java-files)
task (.getTask compiler nil file-mgr listener options nil file-objs)
success (.call task)]
(when-not success
(throw (ex-info "Java compilation failed" {})))))))
================================================
FILE: src/main/clojure/clojure/tools/build/tasks/process.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.tools.build.tasks.process
(:require
[clojure.java.io :as jio]
[clojure.java.process :as proc]
[clojure.tools.deps :as deps]
[clojure.tools.build.api :as api]
[clojure.string :as str])
(:import
[java.io File]))
(set! *warn-on-reflection* true)
(defn- trim-blank [^String s]
(if (str/blank? s) nil s))
(defn process
"Exec the command made from command-args, redirect out and err as directed,
and return {:exit exit-code, :out captured-out, :err captured-err}
Options:
:command-args - required, coll of string args
:dir - directory to run the command from, default current directory
:out - one of :inherit :capture :write :append :ignore
:err - one of :inherit :capture :write :append :ignore
:out-file - file path to write if :out is :write or :append
:err-file - file path to write if :err is :write or :append
:env - map of environment variables to set
The :out and :err input flags take one of the following options:
:inherit - inherit the stream and write the subprocess io to this process's stream (default)
:capture - capture the stream to a string and return it
:write - write to :out-file or :err-file
:append - append to :out-file or :err-file
:ignore - ignore the stream"
[{:keys [command-args dir env out err out-file err-file]
:or {dir ".", out :inherit, err :inherit} :as opts}]
(when (not (seq command-args))
(throw (ex-info "process missing required arg :command-args" opts)))
(let [stream-opt (fn [opt file]
(case opt
:inherit :inherit
:write (proc/to-file (api/resolve-path file))
:append (proc/to-file (api/resolve-path file) :append true)
:ignore :discard
(:capture :pipe) :pipe))
proc-opts {:dir (api/resolve-path (or dir "."))
:out (stream-opt out out-file)
:err (stream-opt err err-file)
:env env}
proc (apply proc/start proc-opts command-args)
out-f (when (= out :capture) (proc/io-task #(slurp (proc/stdout proc))))
err-f (when (= err :capture) (proc/io-task #(slurp (proc/stderr proc))))
exit (deref (proc/exit-ref proc))
out-str (when out-f (trim-blank @out-f))
err-str (when err-f (trim-blank @err-f))]
(cond-> {:exit exit}
out-str (assoc :out out-str)
err-str (assoc :err err-str))))
(comment
(api/process {:command-args ["ls" "-l"]})
(api/process {:command-args ["git" "log"] :out :ignore})
(api/process {:command-args ["java" "-version"] :err :capture})
(api/process {:command-args ["java" "--version"] :out :capture})
(api/process {:env {"FOO" "hi"}
:command-args ["echo" "$FOO"]
:out :capture})
)
(defn- need-cp-file
[os-name java-version command-length]
(and
;; this is only an issue on Windows
(str/starts-with? os-name "Win")
;; CLI support only exists in Java 9+, for Java <= 1.8, the version number is 1.x
(not (str/starts-with? java-version "1."))
;; the actual limit on windows is 8191 (<8k), but giving some room
(> command-length 8000)))
(defn- make-java-args
[java-cmd java-opts cp main main-args use-cp-file]
(let [full-args (vec (concat [java-cmd] java-opts ["-cp" cp (name main)] main-args))
arg-str (str/join " " full-args)]
(if (or (= use-cp-file :always)
(and (= use-cp-file :auto)
(need-cp-file (System/getProperty "os.name") (System/getProperty "java.version") (count arg-str))))
(let [cp-file (doto (File/createTempFile "tbuild-" ".cp") (.deleteOnExit))]
(spit cp-file cp)
(vec (concat [java-cmd] java-opts ["-cp" (str "@" (.getAbsolutePath cp-file)) (name main)] main-args)))
full-args)))
(defn which
"Given the name of an executable, return either a full path to
its location on the system PATH or nil if not found"
[cmd]
(when-let [path (System/getenv "PATH")]
(let [paths (str/split path (re-pattern File/pathSeparator))]
(loop [paths paths]
(when-first [p paths]
(let [f (jio/file p cmd)]
(if (and (.isFile f) (.canExecute f))
(.getCanonicalPath f)
(recur (rest paths)))))))))
(defn- windows?
[]
(str/starts-with? (System/getProperty "os.name") "Windows"))
(defn- java-exe
[]
(if (windows?) "java.exe" "java"))
(defn- java-home-bin
"Returns path $JAVA_HOME/bin/java if JAVA_HOME set, or nil"
[]
(when-let [jhome (System/getenv "JAVA_HOME")]
(let [exe (jio/file jhome "bin" (java-exe))]
(when (and (.exists exe) (.canExecute exe))
(.getCanonicalPath exe)))))
(defn java-executable
"Given the environment, emulate the Clojure CLI logic to determine the
Java executable path and return it by trying in order:
$JAVA_CMD
java on the PATH
$JAVA_HOME/bin/java"
[]
(or
(System/getenv "JAVA_CMD")
(which (java-exe))
(java-home-bin)
(throw (ex-info "Couldn't find java executable via $JAVA_CMD, $PATH, or $JAVA_HOME" {}))))
(defn java-command
"Create Java command line args. The classpath will be the combination of
:cp followed by the classpath from the basis, both are optional.
Options:
:java-cmd - Java command, default = $JAVA_CMD or 'java' on $PATH, or $JAVA_HOME/bin/java
:cp - coll of string classpath entries, used first (if provided)
:basis - runtime basis used for classpath, used last (if provided)
:java-opts - coll of string jvm opts
:main - required, main class symbol
:main-args - coll of main class args
:use-cp-file - one of:
:auto (default) - use only if os=windows && Java >= 9 && command length >= 8k
:always - always write classpath to temp file and include
:never - never write classpath to temp file (pass on command line)
Returns:
:command-args - coll of command arg strings"
[{:keys [java-cmd cp basis java-opts main main-args use-cp-file]
:or {use-cp-file :auto} :as _params}]
(let [cmd (or java-cmd (java-executable))
{:keys [classpath-roots argmap]} basis
cp-entries (concat cp classpath-roots)
cp-str (deps/join-classpath cp-entries)
combined-java-opts (concat java-opts (:jvm-opts argmap))]
{:command-args (make-java-args cmd combined-java-opts cp-str main main-args use-cp-file)}))
================================================
FILE: src/main/clojure/clojure/tools/build/tasks/uber.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.tools.build.tasks.uber
(:require
[clojure.java.io :as jio]
[clojure.pprint :as pprint]
[clojure.set :as set]
[clojure.string :as str]
[clojure.tools.build.api :as api]
[clojure.tools.build.util.file :as file]
[clojure.tools.build.util.zip :as zip])
(:import
[java.io File InputStream IOException OutputStream ByteArrayOutputStream]
[java.nio.file Files]
[java.nio.file.attribute FileAttribute FileTime]
[java.util.jar JarEntry JarInputStream JarOutputStream Manifest]))
(set! *warn-on-reflection* true)
(def ^:private uber-exclusions
[#"project.clj"
#"META-INF/.*\.(?:SF|RSA|DSA|MF)"
#"module-info\.class"
#"(.*/)?\.DS_Store" ;; Mac metadata
#".+~" ;; emacs backup files
#".#.*" ;; emacs
#"(.*/)?\.keep" ;; convention in dirs to keep that are empty
#".*\.pom"
#"(?i)META-INF/(?:INDEX\.LIST|DEPENDENCIES)(?:\.txt)?"])
(defn- exclude-from-uber?
[exclude-patterns ^String path]
(loop [[re & res] exclude-patterns]
(if re
(if (re-matches re path)
true
(recur res))
false)))
(defn- copy-stream!
"Copy input stream to output stream using buffer.
Caller is responsible for passing buffered streams and closing streams."
[^InputStream is ^OutputStream os ^bytes buffer]
(loop []
(let [size (.read is buffer)]
(if (pos? size)
(do
(.write os buffer 0 size)
(recur))
(.close os)))))
(defn- stream->string
[^InputStream is]
(let [baos (ByteArrayOutputStream. 4096)
_ (copy-stream! is baos (byte-array 4096))]
(.toString baos "UTF-8")))
(defn conflict-overwrite
[{:keys [path in]}]
{:write {path {:stream in}}})
(defn- conflict-append
[{:keys [path in]}]
{:write {path {:string (str "\n" (stream->string in)), :append true}}})
(defn- conflict-append-dedupe
[{:keys [path in ^File existing state] :as _params}]
(let [existing-content (slurp existing)
existing-lower (str/lower-case existing-content)
new-content (stream->string in)
new-content-lower (str/lower-case new-content)
seen (or (get-in state [:append-dedupe path]) #{existing-lower})]
(if (contains? seen new-content-lower)
;; already seen
{:state (assoc-in state [:append-dedupe path] seen)}
;; record and append
{:state (assoc-in state [:append-dedupe path] (conj seen new-content))
:write {path {:string (str "\n" new-content), :append true}}})))
(defn conflict-data-readers
[{:keys [path in ^File existing]}]
(binding [*read-eval* false]
(let [existing-str (slurp existing)
existing-reader-fns (read-string
{:read-cond :preserve :features #{:clj}}
existing-str)
append-reader-fns (read-string
{:read-cond :preserve :features #{:clj}}
(stream->string in))
reader-str (with-out-str (pprint/pprint (merge existing-reader-fns append-reader-fns)))]
{:write {path {:string reader-str}}})))
(defn- conflict-warn
[{:keys [path lib]}]
(println "Conflicting path at" path "from" lib))
(defn- conflict-error
[{:keys [path lib]}]
(throw (ex-info (str "Conflicting path at " path " from " lib) {})))
(defn- handler-emit
[^FileTime last-modified-time buffer out-dir path write-spec]
(let [{:keys [string stream append] :or {append false}} write-spec
out-file (jio/file out-dir path)]
(if string
(spit out-file string :append ^boolean append)
(copy-stream! ^InputStream stream (jio/output-stream out-file :append append) buffer))
(Files/setLastModifiedTime (.toPath out-file) last-modified-time)))
(defn- handle-conflict
[handlers last-modified-time buffer out-dir {:keys [state path] :as handler-params}]
(let [use-handler (loop [[[re handler] & hs] (dissoc handlers :default)]
(if re
(if (re-matches re path)
handler
(recur hs))
(:default handlers)))]
(if use-handler
(let [{new-state :state, write :write} (use-handler handler-params)]
(when write
(doseq [[path write-spec] write]
(handler-emit last-modified-time buffer out-dir path write-spec)))
(or new-state state))
(throw (ex-info (format "No handler found for conflict at %s" path) {})))))
(defn- ensure-dir
"Returns true if parent dir exists, false if exists but is not a file,
and throws if it cannot be created."
[^File parent ^File child]
(if (.exists parent)
(.isDirectory parent)
(if (jio/make-parents child)
true
(throw (ex-info (str "Unable to create parent dirs for: " (.toString child)) {})))))
(defn- explode1
"Given one entry/src file, copy to target pursuant to excludes and handlers.
Returns possibly updated state for further exploding."
[^InputStream is ^String path dir? ^FileTime last-modified-time
^File out-file lib {:keys [out-dir buffer exclude handlers] :as context} state]
(cond
;; excluded or directory - do nothing
(or (exclude-from-uber? exclude path) dir?)
state
;; conflict, same file from multiple sources - handle
(.exists out-file)
(handle-conflict handlers last-modified-time buffer out-dir
{:lib lib, :path path, :in is, :existing out-file, :state state})
;; write new file, parent dir exists for writing
(ensure-dir (.getParentFile out-file) out-file)
(do
(copy-stream! ^InputStream is (jio/output-stream out-file) buffer)
(Files/setLastModifiedTime (.toPath out-file) last-modified-time)
state)
:parent-dir-is-a-file
(throw (ex-info (format "Cannot write %s from %s as parent dir is a file from another lib. One of them must be excluded."
path lib) {}))))
(defn- explode
[^File lib-file lib {:keys [out-dir buffer exclude handlers] :as context} state]
(cond
(not (.exists lib-file))
state
(str/ends-with? (.getPath lib-file) ".jar")
(with-open [jis (JarInputStream. (jio/input-stream lib-file))]
(loop [the-state state]
(if-let [entry (.getNextJarEntry jis)]
(let [path (.getName entry)
;; should rarely happen (except /), but chop to make relative:
path (if (str/starts-with? path "/") (subs path 1) path)
out-file (jio/file out-dir path)]
(recur
(explode1 jis path (.isDirectory entry) (.getLastModifiedTime ^JarEntry entry)
out-file lib context the-state)))
the-state)))
(.isDirectory lib-file)
(let [source-dir (.getAbsoluteFile lib-file)
source-path (.toPath source-dir)
fs (file/collect-files source-dir :dirs true)]
(loop [[^File f & restf] fs, the-state state]
(if f
(let [is (when (.isFile f)
(try
(jio/input-stream f)
(catch IOException e
(throw (ex-info (str "Uber task found file but can't read its content in " lib " at path " (.getPath f))
{:path (.getPath f)} e)))))
new-state (try
(let [path (str/replace (.toString (.relativize source-path (.toPath f))) \\ \/)
source-time (FileTime/fromMillis (.lastModified f))
out-file (jio/file out-dir path)]
(explode1 is path (.isDirectory f) source-time out-file lib context the-state))
(finally
(when is (.close ^InputStream is))))]
(recur restf new-state))
the-state)))
:else
(throw (ex-info (format "Unexpected lib file: %s" (.toString lib-file)) {}))))
(defn- remove-optional
"Remove optional libs and their transitive dependencies from the lib tree.
Only remove transitive if all dependents are optional."
[libs]
(let [by-opt (group-by (fn [[_lib coord]] (boolean (:optional coord))) libs)
optional (apply conj {} (get by-opt true))]
(if (seq optional)
(loop [req (get by-opt false)
opt optional]
(let [under-opts (group-by (fn [[_lib {:keys [dependents]}]]
(boolean
(and (seq dependents)
(set/subset? (set dependents) (set (keys opt))))))
req)
trans-opt (get under-opts true)]
(if (seq trans-opt)
(recur (get under-opts false) (into opt trans-opt))
(apply conj {} req))))
libs)))
(defn- built-ins
[kw]
(or
(get {:ignore (fn [_])
:overwrite conflict-overwrite
:append conflict-append
:append-dedupe conflict-append-dedupe
:data-readers conflict-data-readers
:warn conflict-warn
:error conflict-error}
kw)
(throw (ex-info (str "Invalid handler: " kw) {}))))
(defn- prep-handler
"Convert user handler to fn"
[handler]
(cond
(keyword? handler) (built-ins handler)
(symbol? handler) (deref (requiring-resolve handler))
(ifn? handler) handler
:else (throw (ex-info (str "Invalid handler: " handler) {}))))
(def ^:private default-handlers
{"^data_readers.clj[c]?$" :data-readers
"^META-INF/services/.*" :append
"(?i)^(META-INF/)?(COPYRIGHT|NOTICE|LICENSE)(\\.(txt|md))?$" :append-dedupe
:default :ignore})
(defn- prep-handlers
"Transform user handler map into a map of re->fn"
[handlers]
(reduce-kv
(fn [m pattern handler]
(assoc m (if (= pattern :default) :default (re-pattern pattern))
(prep-handler handler)))
{} (merge default-handlers handlers)))
(defn uber
[{mf-attrs :manifest, :keys [basis class-dir uber-file main exclude conflict-handlers]}]
(let [working-dir (.toFile (Files/createTempDirectory "uber" (into-array FileAttribute [])))
context {:out-dir working-dir
:buffer (byte-array 4096)
:handlers (prep-handlers conflict-handlers)
:exclude (map re-pattern (into uber-exclusions exclude))}]
(try
(let [{:keys [libs]} basis
compile-dir (api/resolve-path class-dir)
manifest (Manifest.)
uber-file (api/resolve-path uber-file)
mf-attr-strs (reduce-kv (fn [m k v] (assoc m (str k) (str v))) nil mf-attrs)]
(reduce
(fn [state [lib coord]]
(reduce
(fn [state path] (explode (jio/file path) lib context state))
state (:paths coord)))
nil ;; initial state, usable by handlers if needed
(assoc (remove-optional libs) nil {:paths [compile-dir]}))
(zip/fill-manifest! manifest
(merge
(cond->
{"Manifest-Version" "1.0"
"Created-By" "org.clojure/tools.build"
"Build-Jdk-Spec" (System/getProperty "java.specification.version")}
main (assoc "Main-Class" (str/replace (str main) \- \_))
(.exists (jio/file working-dir "META-INF" "versions")) (assoc "Multi-Release" "true"))
mf-attr-strs))
(file/ensure-dir (.getParent uber-file))
(with-open [jos (JarOutputStream. (jio/output-stream uber-file) manifest)]
(zip/copy-to-zip jos working-dir)))
(finally
(file/delete working-dir)))))
================================================
FILE: src/main/clojure/clojure/tools/build/tasks/write_pom.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.tools.build.tasks.write-pom
(:require
[clojure.java.io :as jio]
[clojure.string :as str]
[clojure.data.xml :as xml]
[clojure.data.xml.tree :as tree]
[clojure.data.xml.event :as event]
[clojure.walk :as walk]
[clojure.zip :as zip]
[clojure.tools.deps.util.maven :as maven]
[clojure.tools.deps.util.io :refer [printerrln]]
[clojure.tools.build.api :as api]
[clojure.tools.build.util.file :as file])
(:import [clojure.data.xml.node Element]
[java.io Reader]
[java.time Instant ZoneId ZonedDateTime]
[java.time.format DateTimeFormatter]))
(set! *warn-on-reflection* true)
(xml/alias-uri 'pom "http://maven.apache.org/POM/4.0.0")
(def ^:private pom-ns (name (.-name ^clojure.lang.Namespace (get (ns-aliases *ns*) 'pom))))
(defn- to-dep
[[lib {:keys [mvn/version exclusions optional] :as coord}]]
(let [[group-id artifact-id classifier] (maven/lib->names lib)]
(if version
(cond->
[::pom/dependency
[::pom/groupId group-id]
[::pom/artifactId artifact-id]
[::pom/version version]]
classifier
(conj [::pom/classifier classifier])
(seq exclusions)
(conj [::pom/exclusions
(map (fn [excl]
[::pom/exclusion
[::pom/groupId (or (namespace excl) (name excl))]
[::pom/artifactId (name excl)]])
exclusions)])
optional
(conj [::pom/optional "true"]))
(printerrln "Skipping coordinate:" coord))))
(defn- gen-deps
[deps]
[::pom/dependencies
(map to-dep deps)])
(defn- gen-source-dir
[path]
[::pom/sourceDirectory path])
(defn- to-resource
[resource]
[::pom/resource
[::pom/directory resource]])
(defn- gen-resources
[rpaths]
[::pom/resources
(map to-resource rpaths)])
(defn- to-repo-policy
[parent-tag {:keys [enabled update checksum]}]
[parent-tag
(when (some? enabled) [::pom/enabled (str enabled)])
(when update [::pom/updatePolicy (if (keyword? update) (name update) (str "interval:" update))])
(when checksum [::pom/checksumPolicy (name checksum)])])
(defn- to-repo
[[name {:keys [url snapshots releases]}]]
[::pom/repository
[::pom/id name]
[::pom/url url]
(when releases (to-repo-policy ::pom/releases releases))
(when snapshots (to-repo-policy ::pom/snapshots snapshots))])
(defn- gen-repos
[repos]
[::pom/repositories
(map to-repo repos)])
(defn- pomify
[val]
(if (and (vector? val) (keyword? (first val)))
(into [(keyword pom-ns (name (first val)))] (rest val))
val))
(defn- gen-pom
[{:keys [deps src-paths resource-paths repos group artifact version scm pom-data]
:or {version "0.1.0"}}]
(let [[path & paths] src-paths
{:keys [connection developerConnection tag url]} scm]
(xml/sexp-as-element
(into
[::pom/project
{:xmlns "http://maven.apache.org/POM/4.0.0"
(keyword "xmlns:xsi") "http://www.w3.org/2001/XMLSchema-instance"
(keyword "xsi:schemaLocation") "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"}
[::pom/modelVersion "4.0.0"]
[::pom/packaging "jar"]
[::pom/groupId group]
[::pom/artifactId artifact]
[::pom/version version]
[::pom/name artifact]
(gen-deps deps)
(when (or path (seq resource-paths))
(when (seq paths) (apply printerrln "Skipping paths:" paths))
[::pom/build
(when path (gen-source-dir path))
(when (seq resource-paths) (gen-resources resource-paths))])
(gen-repos repos)
(when scm
[::pom/scm
(when connection [::pom/connection connection])
(when developerConnection [::pom/developerConnection developerConnection])
(when tag [::pom/tag tag])
(when url [::pom/url url])])]
(walk/postwalk pomify pom-data)))))
(defn- make-xml-element
[{:keys [tag attrs] :as node} children]
(with-meta
(apply xml/element tag attrs children)
(meta node)))
(defn- xml-update
[root tag-path replace-node]
(let [z (zip/zipper xml/element? :content make-xml-element root)]
(zip/root
(loop [[tag & more-tags :as tags] tag-path, parent z, child (zip/down z)]
(if child
(if (= tag (:tag (zip/node child)))
(if (seq more-tags)
(recur more-tags child (zip/down child))
(zip/edit child (constantly replace-node)))
(if-let [next-sibling (zip/right child)]
(recur tags parent next-sibling)
(if (seq more-tags)
(let [new-parent (zip/append-child parent (xml/sexp-as-element tag))
new-child (zip/rightmost (zip/down new-parent))]
(recur more-tags new-child (zip/down new-child)))
(zip/append-child parent replace-node))))
(if (seq more-tags)
(let [new-parent (zip/append-child parent (xml/sexp-as-element tag))
new-child (zip/rightmost (zip/down new-parent))]
(recur more-tags new-child (zip/down new-child)))
(zip/append-child parent replace-node)))))))
(defn- replace-deps
[pom deps]
(xml-update pom [::pom/dependencies] (xml/sexp-as-element (gen-deps deps))))
(defn- replace-paths
[pom [path & paths]]
(if path
(do
(when (seq paths) (apply printerrln "Skipping paths:" paths))
(xml-update pom [::pom/build ::pom/sourceDirectory] (xml/sexp-as-element (gen-source-dir path))))
pom))
(defn- replace-resources
[pom resource-paths]
(if (seq resource-paths)
(xml-update pom [::pom/build ::pom/resources] (xml/sexp-as-element (gen-resources resource-paths)))
pom))
(defn- replace-repos
[pom repos]
(if (seq repos)
(xml-update pom [::pom/repositories] (xml/sexp-as-element (gen-repos repos)))
pom))
(defn- replace-lib
[pom lib]
(if lib
(-> pom
(xml-update [::pom/groupId] (xml/sexp-as-element [::pom/groupId (namespace lib)]))
(xml-update [::pom/artifactId] (xml/sexp-as-element [::pom/artifactId (name lib)]))
(xml-update [::pom/name] (xml/sexp-as-element [::pom/name (name lib)])))
pom))
(defn- replace-version
[pom version]
(if version
(xml-update pom [::pom/version] (xml/sexp-as-element [::pom/version version]))
pom))
(defn- replace-scm
[pom {:keys [connection developerConnection tag url] :as scm}]
(if scm
(cond-> pom
connection (xml-update [::pom/scm ::pom/connection] (xml/sexp-as-element [::pom/connection connection]))
developerConnection (xml-update [::pom/scm ::pom/developerConnection] (xml/sexp-as-element [::pom/developerConnection developerConnection]))
tag (xml-update [::pom/scm ::pom/tag] (xml/sexp-as-element [::pom/tag tag]))
url (xml-update [::pom/scm ::pom/url] (xml/sexp-as-element [::pom/url url])))
pom))
(defn- parse-xml
[^Reader rdr]
(let [roots (tree/seq-tree event/event-element event/event-exit? event/event-node
(xml/event-seq rdr {:include-node? #{:element :characters :comment}
:skip-whitespace true}))]
(first (filter #(instance? Element %) (first roots)))))
(defn- libs->deps
"Convert libmap to root deps"
[libs]
(reduce-kv
(fn [ret lib {:keys [dependents] :as coord}]
(if (seq dependents)
ret
(assoc ret lib coord)))
{} libs))
(defn meta-maven-path
[params]
(let [{:keys [lib]} params
pom-file (jio/file "META-INF" "maven" (namespace lib) (name lib))]
(.toString pom-file)))
(defn write-pom
[params]
(let [{:keys [basis class-dir target src-pom lib version scm src-dirs resource-dirs repos pom-data]} params
{:keys [libs]} basis
root-deps (libs->deps libs)
src-pom-file (when-not (= :none src-pom)
(api/resolve-path (or src-pom "pom.xml")))
repos (or repos (remove #(= "https://repo1.maven.org/maven2/" (-> % val :url)) (:mvn/repos basis)))
pom (if (and src-pom-file (.exists src-pom-file))
(do
(when pom-data
(println "Warning in write-pom: pom-data supplied but not used because pom template exists at" (or src-pom "pom.xml")))
(with-open [rdr (jio/reader src-pom-file)]
(-> rdr
parse-xml
(replace-deps root-deps)
(replace-paths src-dirs)
(replace-resources resource-dirs)
(replace-repos repos)
(replace-lib lib)
(replace-version version)
(replace-scm scm))))
(gen-pom
(cond->
{:deps root-deps
:src-paths src-dirs
:resource-paths resource-dirs
:repos repos
:group (namespace lib)
:artifact (name lib)}
version (assoc :version version)
scm (assoc :scm scm)
pom-data (assoc :pom-data pom-data))))
pom-dir-file (file/ensure-dir
(cond
class-dir (jio/file (api/resolve-path class-dir) (meta-maven-path {:lib lib}))
target (-> target api/resolve-path jio/file file/ensure-dir)
:else (throw (ex-info "write-pom requires either :class-dir or :target" {}))))]
(spit (jio/file pom-dir-file "pom.xml") (xml/indent-str pom))
(spit (jio/file pom-dir-file "pom.properties")
(str/join (System/lineSeparator)
["# Generated by org.clojure/tools.build"
(let [dtf (DateTimeFormatter/ofPattern "E MMM d HH:mm:ss 'UTC' u")
inst (or (some-> "SOURCE_DATE_EPOCH"
System/getenv
parse-long
Instant/ofEpochSecond)
(Instant/now))]
(str "# " (.format dtf (ZonedDateTime/ofInstant inst (ZoneId/of "Z")))))
(format "version=%s" version)
(format "groupId=%s" (namespace lib))
(format "artifactId=%s" (name lib))]))))
================================================
FILE: src/main/clojure/clojure/tools/build/tasks/zip.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.tools.build.tasks.zip
(:require
[clojure.java.io :as jio]
[clojure.tools.build.api :as api]
[clojure.tools.build.util.file :as file]
[clojure.tools.build.util.zip :as zip])
(:import
[java.util.zip ZipOutputStream]))
(set! *warn-on-reflection* true)
(defn zip
[{:keys [src-dirs zip-file] :as params}]
(let [zip-file (api/resolve-path zip-file)]
(file/ensure-dir (.getParent zip-file))
(with-open [zos (ZipOutputStream. (jio/output-stream zip-file))]
(doseq [zpath src-dirs]
(let [zip-from (file/ensure-dir (api/resolve-path zpath))]
;(println "Zipping from" (.getPath zip-from) "to" (.getPath zip-file))
(zip/copy-to-zip zos zip-from))))))
(defn unzip
[{:keys [zip-file target-dir] :as params}]
(let [{:keys [zip-file target-dir]} params
ret (zip/unzip (api/resolve-path zip-file) (api/resolve-path target-dir))]
(when-not ret
(throw (ex-info (format "Zip file does not exist: %s" zip-file) {})))))
================================================
FILE: src/main/clojure/clojure/tools/build/util/file.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.tools.build.util.file
(:require
[clojure.java.io :as jio]
[clojure.string :as str])
(:import
[java.io File]
[java.nio.file Path Files LinkOption CopyOption StandardCopyOption]
[clojure.lang PersistentQueue]))
(set! *warn-on-reflection* true)
(defn collect-files
"Recursively collect all paths under path, starting from root.
Options:
:dirs - whether to collect directories (default false)
:collect - function for whether to collect a path (default yes)"
[^File root & {:keys [dirs collect]
:or {dirs false
collect (constantly true)}}]
(when (.exists root)
(loop [queue (conj PersistentQueue/EMPTY root)
collected []]
(let [^File file (peek queue)]
(if file
(let [path (.toPath file)
is-dir (Files/isDirectory path (into-array LinkOption [LinkOption/NOFOLLOW_LINKS]))
children (when is-dir (with-open [entries (Files/newDirectoryStream path)]
(mapv #(.toFile ^Path %) entries)))
collect? (and (if is-dir dirs true) (collect file))]
(recur (into (pop queue) children) (if collect? (conj collected file) collected)))
(when (seq collected) collected))))))
(defn suffixes
"Returns a predicate matching suffixes"
[& suffixes]
(apply some-fn
(map #(fn [^File f] (str/ends-with? (.toString f) ^String %)) suffixes)))
(defn delete
"Recursively delete file, where file is coerced with clojure.java.io/file"
[file]
(run! #(.delete ^File %) (reverse (collect-files (jio/file file) :dirs true))))
(def ^{:private true, :tag "[Ljava.nio.file.CopyOption;"}
copy-options
(into-array CopyOption [StandardCopyOption/COPY_ATTRIBUTES StandardCopyOption/REPLACE_EXISTING]))
(defn copy-file
"Copy file from src to target, retaining file attributes. Returns nil."
[^File src-file ^File target-file]
(.mkdirs target-file)
(Files/copy (.toPath src-file) (.toPath target-file) copy-options)
nil)
(defn copy-contents
"Copy files in src dir to target dir, optionally filtering by prefix paths"
([^File src-dir ^File target-dir]
(let [source-path (.toPath src-dir)
target-path (.toPath target-dir)
source-files (collect-files src-dir)]
;(println "source" (str source-path))
;(println "target" (str target-path))
;(println "source-files" (map str source-files))
(run!
(fn [^File f]
(let [p (.toPath f)
new-path (.resolve target-path (.relativize source-path p))]
;(println "copying" (str p) (str new-path))
(copy-file f (.toFile new-path))))
source-files)))
([^File src-dir ^File target-dir prefixes]
(when (.exists src-dir)
(let [root (.toPath src-dir)
target (.toPath target-dir)]
(loop [queue (conj PersistentQueue/EMPTY src-dir)]
(let [^File file (peek queue)]
(when file
(let [path (.toPath file)
relative (.relativize root path)]
;(println "consider" (.toString file) "match" (some #(str/starts-with? (.toString relative) %) prefixes) "dir" (Files/isDirectory path (into-array LinkOption [LinkOption/NOFOLLOW_LINKS])))
(cond
;; match, copy this file/dir
(some #(str/starts-with? (.toString relative) %) prefixes)
(let [end-path (.resolve target relative)]
(copy-contents file (.toFile end-path))
(recur (pop queue)))
;; no match, but continue looking in this directory if it could match later
(and
(Files/isDirectory path (into-array LinkOption [LinkOption/NOFOLLOW_LINKS]))
(some #(str/starts-with? % (.toString relative)) prefixes))
(recur (into (pop queue)
(with-open [entries (Files/newDirectoryStream path)]
(mapv #(.toFile ^Path %) entries))))
;; work the queue
:else
(recur (pop queue)))))))))))
(defn ensure-dir
"Ensure dir exists by making all parent directories and return it"
^File [dir]
(let [d (jio/file dir)]
(if (.exists d)
d
(if (.mkdirs d)
d
(throw (ex-info (str "Can't create directory " dir) {}))))))
(defn ensure-file
([file] (ensure-file file ""))
([file contents & opts]
(let [file (jio/file file)
parent (.getParent file)]
(if (.exists (jio/file parent))
(apply spit file contents opts)
(do
(ensure-dir parent)
(apply spit file contents opts))))))
================================================
FILE: src/main/clojure/clojure/tools/build/util/log.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.tools.build.util.log
(:require
[clojure.pprint :as pprint]))
(defmacro log
[verbose & msgs]
`(when ~verbose
(println ~@msgs)))
(defmacro log-map
[verbose m]
`(when ~verbose
(binding [*print-namespace-maps* false]
(pprint/pprint ~m))))
================================================
FILE: src/main/clojure/clojure/tools/build/util/zip.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.tools.build.util.zip
(:require
[clojure.java.io :as jio]
[clojure.tools.build.util.file :as file]
[clojure.string :as str])
(:import
[java.io File InputStream OutputStream]
[java.nio.file Files LinkOption]
[java.nio.file.attribute BasicFileAttributes]
[java.util.zip ZipFile ZipInputStream ZipOutputStream ZipEntry]
[java.util.jar Manifest Attributes$Name]))
(set! *warn-on-reflection* true)
(defn- add-zip-entry
[^ZipOutputStream output-stream ^String path ^File file]
(let [dir (.isDirectory file)
attrs (Files/readAttributes (.toPath file) BasicFileAttributes ^"[Ljava.nio.file.LinkOption;" (into-array LinkOption []))
path (if (and dir (not (.endsWith path "/"))) (str path "/") path)
path (str/replace path \\ \/) ;; only use unix-style paths in jars
entry (doto (ZipEntry. path)
;(.setSize (.size attrs))
;(.setLastAccessTime (.lastAccessTime attrs))
(.setLastModifiedTime (.lastModifiedTime attrs)))]
(.putNextEntry output-stream entry)
(when-not dir
(with-open [fis (jio/input-stream file)]
(jio/copy fis output-stream)))
(.closeEntry output-stream)))
(defn copy-to-zip
[^ZipOutputStream jos ^File root]
(let [root-path (.toPath root)
files (file/collect-files root :dirs true)]
(run! (fn [^File f]
(let [rel-path (.toString (.relativize root-path (.toPath f)))]
(when-not (= rel-path "")
;(println " Adding" rel-path)
(add-zip-entry jos rel-path f))))
files)))
(defn fill-manifest!
[^Manifest manifest props]
(let [attrs (.getMainAttributes manifest)]
(run!
(fn [[name value]]
(.put attrs (Attributes$Name. ^String name) value)) props)))
(defn list-zip
[^String zip-path]
(let [zip-file (jio/file zip-path)]
(when (.exists zip-file)
(with-open [zip (ZipFile. zip-file)]
(let [entries (enumeration-seq (.entries zip))]
(sort-by :name
(into [] (map (fn [^ZipEntry entry]
{:name (.getName entry)
:created (.getCreationTime entry)
:modified (.getLastModifiedTime entry)
:size (.getSize entry)}))
entries)))))))
(defn copy-stream!
"Copy input stream to output stream using buffer.
Caller is responsible for passing buffered streams and closing streams."
[^InputStream is ^OutputStream os ^bytes buffer]
(loop []
(let [size (.read is buffer)]
(if (pos? size)
(do
(.write os buffer 0 size)
(recur))
(.close os)))))
(defn unzip
[^String zip-path ^String target-dir]
(let [buffer (byte-array 4096)
zip-file (jio/file zip-path)]
(if (.exists zip-file)
(with-open [zis (ZipInputStream. (jio/input-stream zip-file))]
(loop []
(if-let [entry (.getNextEntry zis)]
;(println "entry:" (.getName entry) (.isDirectory entry))
(let [out-file (jio/file target-dir (.getName entry))]
(jio/make-parents out-file)
(when-not (.isDirectory entry)
(with-open [output (jio/output-stream out-file)]
(copy-stream! zis output buffer)
(Files/setLastModifiedTime (.toPath out-file) (.getLastModifiedTime entry))))
(recur))
true)))
false)))
================================================
FILE: src/test/clojure/clojure/tools/build/tasks/test_basis.clj
================================================
(ns clojure.tools.build.tasks.test-basis
(:require
[clojure.test :refer :all]
[clojure.tools.build.api :as api]
[clojure.tools.build.test-util :refer :all])
(:import
[java.nio.file Files]
[java.nio.file.attribute FileAttribute]))
(deftest test-missing-project-deps-file
(let [path (Files/createTempDirectory "abc" (make-array FileAttribute 0))]
(with-test-dir (.toString path)
(api/set-project-root! (.getAbsolutePath *test-dir*))
(api/create-basis {}))))
(comment
(run-tests)
)
================================================
FILE: src/test/clojure/clojure/tools/build/tasks/test_compile_clj.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.tools.build.tasks.test-compile-clj
(:require
[clojure.test :refer :all :as test]
[clojure.java.io :as jio]
[clojure.string :as str]
[clojure.tools.build.api :as api]
[clojure.tools.build.tasks.compile-clj :as compile-clj]
[clojure.tools.build.test-util :refer :all]))
(deftest test-topo
;; deps: a -> b -> c, d
;; expect: c b a (reverse topo sort), then d at the end
(is (= '[c b a d]
(#'compile-clj/nses-in-topo [(jio/file "test-data/nses/src")]))))
(deftest test-compile
(with-test-dir "test-data/p1"
(api/set-project-root! (.getAbsolutePath *test-dir*))
(api/compile-clj {:class-dir "target/classes"
:src-dirs ["src"]
:basis (api/create-basis nil)})
(is (true? (.exists (jio/file (project-path "target/classes/foo/bar.class")))))
(is (true? (.exists (jio/file (project-path "target/classes/foo/bar__init.class")))))
(is (true? (.exists (jio/file (project-path "target/classes/foo/bar$hello.class")))))))
;; use :src-dirs from basis paths
(deftest test-compile-basis-paths
(with-test-dir "test-data/p1"
(api/set-project-root! (.getAbsolutePath *test-dir*))
(api/compile-clj {:class-dir "target/classes"
:basis (api/create-basis nil)})
(is (true? (.exists (jio/file (project-path "target/classes/foo/bar.class")))))
(is (true? (.exists (jio/file (project-path "target/classes/foo/bar__init.class")))))
(is (true? (.exists (jio/file (project-path "target/classes/foo/bar$hello.class")))))))
(defn find-java []
(-> (api/process {:command-args [(if windows? "where" "which") "java"]
:out :capture})
:out
str/split-lines
first))
(deftest test-compile-passthrough-opts
(when-not (str/starts-with? (System/getProperty "java.version") "1.")
(let [java-cmd (find-java)]
(with-test-dir "test-data/p1"
(api/set-project-root! (.getAbsolutePath *test-dir*))
(api/compile-clj {:class-dir "target/classes"
:src-dirs ["src"]
:basis (api/create-basis nil)
;; pass these through to java command
:java-opts ["-Dhi=there"]
:use-cp-file :always
:java-cmd java-cmd})
(is (true? (.exists (jio/file (project-path "target/classes/foo/bar.class")))))
(is (true? (.exists (jio/file (project-path "target/classes/foo/bar__init.class")))))
(is (true? (.exists (jio/file (project-path "target/classes/foo/bar$hello.class")))))))))
(deftest test-turn-off-assert-with-bindings
(with-test-dir "test-data/assert"
(api/set-project-root! (.getAbsolutePath *test-dir*))
(let [basis (api/create-basis nil)
invoke #(-> {:basis basis :main 'clojure.main :main-args ["-e" "((requiring-resolve 'foo.check-assert/f) 100)"]}
api/java-command
(merge {:out :capture, :err :ignore})
api/process)
compile-params {:class-dir "target/classes" :src-dirs ["src"] :basis basis}]
;; by default, assertions are on when compiling, then invocation fails (assertion expects keyword)
(api/compile-clj compile-params) ;; no :bindings set
(is (= {:exit 1} (invoke)))
;; recompile with binding to turn off assertions, then it passes (assertion not checked)
(api/delete {:path "target/classes"})
(api/compile-clj (assoc compile-params :bindings {#'clojure.core/*assert* false})) ;; turn off asserts
(is (= {:exit 0, :out (str "100" (System/lineSeparator))} (invoke))))))
(deftest test-capture-reflection
(with-test-dir "test-data/reflecting"
(api/set-project-root! (.getAbsolutePath *test-dir*))
(let [basis (api/create-basis nil)
compile-params {:class-dir "target/classes"
:src-dirs ["src"]
:basis basis
:ns-compile ['foo.bar]}]
;; by default, reflection does not warn
(is (nil? (api/compile-clj compile-params))) ;; no :bindings set
;; compile with reflection warnings and capture the error output
(api/delete {:path "target/classes"})
(is (str/starts-with?
(:err
(api/compile-clj (merge compile-params
{:bindings {#'clojure.core/*warn-on-reflection* true}
:err :capture})))
"Reflection warning")))))
(deftest test-accidental-basis-delay
(with-test-dir "test-data/p1"
(api/set-project-root! (.getAbsolutePath *test-dir*))
(is (thrown? clojure.lang.ExceptionInfo
(api/compile-clj {:class-dir "target/classes"
:src-dirs ["src"]
:basis (delay (api/create-basis nil))})))))
(comment
(run-tests)
)
================================================
FILE: src/test/clojure/clojure/tools/build/tasks/test_copy.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.tools.build.tasks.test-copy
(:require
[clojure.test :refer :all]
[clojure.java.io :as jio]
[clojure.string :as str]
[clojure.tools.build.api :as api]
[clojure.tools.build.util.file :as file]
[clojure.tools.build.util.zip :as zip]
[clojure.tools.build.test-util :refer :all])
(:import
[java.io File FileInputStream ByteArrayOutputStream]
[java.nio.file Files LinkOption FileSystems]
[java.nio.file.attribute PosixFilePermission]
[java.util UUID]))
(defn slurp-binary
[^File f]
(let [fis (FileInputStream. f)
os (ByteArrayOutputStream.)
buffer (byte-array 4096)]
(zip/copy-stream! fis os buffer)
(.toByteArray os)))
(deftest test-copy
(with-test-dir "test-data/p1"
(let [txt (str (UUID/randomUUID))]
(api/set-project-root! (.getAbsolutePath *test-dir*))
(api/copy-dir {:target-dir "target/classes"
:src-dirs ["src" "resources"]
:replace {"__REPLACE__" txt}})
(let [source-file (jio/file (project-path "target/classes/foo/bar.clj"))
contents (slurp source-file)]
(is (.exists source-file))
(is (str/includes? contents txt)))
;; binary files in replaced exts should be copied but not replaced
(let [binary-in (jio/file (project-path "resources/test.png"))
binary-out (jio/file (project-path "target/classes/test.png"))]
(is (.exists binary-out))
(is (= (seq (slurp-binary binary-in)) (seq (slurp-binary binary-out))))))))
(deftest test-replace-retains-perms
(when (contains? (.supportedFileAttributeViews (FileSystems/getDefault)) "posix")
(with-test-dir "test-data/p1"
(api/set-project-root! (.getAbsolutePath *test-dir*))
(let [start-file (jio/file (project-path "target/x/f"))
_start (file/ensure-file start-file "abc")
start-path (.toPath start-file)]
(Files/setPosixFilePermissions start-path #{PosixFilePermission/OWNER_READ PosixFilePermission/GROUP_READ PosixFilePermission/OWNER_EXECUTE})
(api/copy-dir {:src-dirs [(project-path "target/x")] :target-dir (project-path "target/y") :replace {"abc" "xyz"}})
(let [end (jio/file (project-path "target/y/f"))
end-path (.toPath end)
perms (Files/getPosixFilePermissions end-path (into-array LinkOption [LinkOption/NOFOLLOW_LINKS]))]
(is (= (slurp end) "xyz"))
(is (contains? perms PosixFilePermission/OWNER_EXECUTE)))))))
(comment
(run-tests)
)
================================================
FILE: src/test/clojure/clojure/tools/build/tasks/test_delete.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.tools.build.tasks.test-delete
(:require
[clojure.test :refer :all :as test]
[clojure.java.io :as jio]
[clojure.tools.build.api :as api]
[clojure.tools.build.test-util :refer :all]))
(deftest test-delete
(with-test-dir "test-data/p1"
(api/set-project-root! (.getAbsolutePath *test-dir*))
;; copy src into target, then delete, and check target dir is gone
(api/copy-dir {:target-dir "target/classes"
:src-dirs ["src"]})
(api/delete {:path "target"})
(is (false? (.exists (jio/file (project-path "target/classes")))))))
(comment
(run-tests)
)
================================================
FILE: src/test/clojure/clojure/tools/build/tasks/test_install.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.tools.build.tasks.test-install
(:require
[clojure.java.io :as jio]
[clojure.test :as test :refer [deftest is]]
[clojure.tools.build.api :as api]
[clojure.tools.build.test-util :refer [with-test-dir *test-dir* project-path]]))
(def test-org (str (gensym "ORG")))
(def test-lib (str (gensym "LIB")))
(def lib (symbol test-org test-lib))
(def version "1.0.0")
(deftest test-install-no-pom
(with-test-dir "test-data/p1"
(let [classes "target/classes"
jar-path "target/output.jar"
local-repo (project-path "tmp-repo")
basis (api/create-basis {:project "deps.edn", :extra {:mvn/local-repo local-repo}})]
(api/set-project-root! (.getAbsolutePath *test-dir*))
(api/copy-dir {:src-dirs ["src"], :target-dir classes})
(api/write-pom {:basis basis, :class-dir classes, :lib lib, :version version})
(api/jar {:class-dir classes, :jar-file jar-path})
(api/install {:basis basis
:jar-file jar-path
:lib lib
:class-dir classes
:version version})
(let [expected-dir (jio/file local-repo test-org)
expected-jar (jio/file expected-dir test-lib version (str test-lib "-1.0.0.jar"))
expected-pom (jio/file expected-dir test-lib version (str test-lib "-1.0.0.pom"))]
(is (.exists expected-dir))
(is (.exists expected-jar))
(is (.exists expected-pom))))))
(comment
(test/run-tests)
)
================================================
FILE: src/test/clojure/clojure/tools/build/tasks/test_jar.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.tools.build.tasks.test-jar
(:require
[clojure.string :as str]
[clojure.test :refer :all]
[clojure.java.io :as jio]
[clojure.tools.build.api :as api]
[clojure.tools.build.test-util :refer :all]
[clojure.tools.build.util.zip :as zip])
(:import [java.util.zip ZipFile ZipEntry]))
(defn slurp-manifest
[z]
(let [zip-file (jio/file z)]
(with-open [zip (ZipFile. zip-file)]
(let [^ZipEntry ze (.getEntry zip "META-INF/MANIFEST.MF")]
(when ze
(slurp (.getInputStream zip ze)))))))
(deftest test-jar
(let [jar-path "target/output.jar"]
(with-test-dir "test-data/p1"
(api/set-project-root! (.getAbsolutePath *test-dir*))
(api/jar {:class-dir "src"
:jar-file jar-path})
(is (true? (.exists (jio/file (project-path jar-path)))))
(is (= #{"META-INF/MANIFEST.MF" "foo/" "foo/bar.clj"}
(set (map :name (zip/list-zip (project-path jar-path)))))))))
(deftest test-jar-custom-manifest
(let [jar-path "target/output.jar"]
(with-test-dir "test-data/p1"
(api/set-project-root! (.getAbsolutePath *test-dir*))
(api/jar {:class-dir "src"
:jar-file jar-path
:manifest {"Abc" 100}})
(is (true? (.exists (jio/file (project-path jar-path)))))
(is (= #{"META-INF/MANIFEST.MF" "foo/" "foo/bar.clj"}
(set (map :name (zip/list-zip (project-path jar-path))))))
(let [manifest-out (slurp-manifest (project-path jar-path))]
(is (str/includes? manifest-out "Abc: 100"))))))
(comment
(run-tests)
)
================================================
FILE: src/test/clojure/clojure/tools/build/tasks/test_javac.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.tools.build.tasks.test-javac
(:require
[clojure.string :as str]
[clojure.test :refer :all :as test]
[clojure.java.io :as jio]
[clojure.tools.build.api :as api]
[clojure.tools.build.test-util :refer :all]))
(deftest test-javac
(with-test-dir "test-data/p1"
(api/set-project-root! (.getAbsolutePath *test-dir*))
(api/javac {:class-dir "target/classes"
:src-dirs ["java"]})
(is (true? (.exists (jio/file (project-path "target/classes/foo/Demo1.class")))))
(is (true? (.exists (jio/file (project-path "target/classes/foo/Demo2.class")))))
(let [class-path (.getPath (jio/file (project-path "target/classes")))]
(is (= "Hello" (str/trim (:out (api/process {:command-args ["java" "-cp" class-path "foo.Demo1"] :out :capture})))))
(is (= "Hello" (str/trim (:out (api/process {:command-args ["java" "-cp" class-path "foo.Demo2"] :out :capture}))))))))
(comment
(run-tests)
)
================================================
FILE: src/test/clojure/clojure/tools/build/tasks/test_pom.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.tools.build.tasks.test-pom
(:require
[clojure.test :refer :all :as test]
[clojure.java.io :as jio]
[clojure.string :as str]
[clojure.zip :as zip]
[clojure.data.xml :as xml]
[clojure.tools.build.api :as api]
[clojure.tools.build.tasks.write-pom :as write-pom]
[clojure.tools.build.test-util :refer :all])
(:import
[java.io File InputStream]
[java.util Properties]
[clojure.lang ExceptionInfo]))
(set! *warn-on-reflection* true)
(xml/alias-uri 'pom "http://maven.apache.org/POM/4.0.0")
(defn read-xml
[^File f]
(when (.exists f)
(#'write-pom/parse-xml (jio/reader f))))
(defn xml-path-val
[root tag-path]
(let [z (zip/zipper xml/element? :content nil root)]
(loop [[tag & more-tags :as tags] tag-path, parent z, child (zip/down z)]
(if child
(let [node (zip/node child)]
(if (= tag (:tag node))
(if (seq more-tags)
(recur more-tags child (zip/down child))
(:content node))
(recur tags parent (zip/right child))))
nil))))
(defn read-props
[^File f]
(let [props (Properties.)]
(when (.exists f)
(doto props
(.load ^InputStream (jio/input-stream f))))))
(deftest test-new-pom
(with-test-dir "test-data/p1"
(api/set-project-root! (.getAbsolutePath *test-dir*))
(api/delete {:path "target"})
(api/write-pom {;; NO :src-pom
:lib 'test/p1
:version "1.2.3"
:class-dir "target/classes"
:src-dirs ["src"]
:resource-dirs ["resources"]
:basis (api/create-basis nil)
:scm {:tag "v1.2.3" :url "https://github.com/example/p1"}})
(let [pom-dir (jio/file (project-path "target/classes/META-INF/maven/test/p1"))
pom-out (jio/file pom-dir "pom.xml")
pom (read-xml pom-out)
prop-out (jio/file pom-dir "pom.properties")
props (read-props prop-out)]
;; check xml out
(is (.exists pom-out))
(are [path val] (= val (xml-path-val pom path))
[::pom/packaging] ["jar"]
[::pom/groupId] ["test"]
[::pom/artifactId] ["p1"]
[::pom/version] ["1.2.3"]
[::pom/name] ["p1"]
[::pom/build ::pom/sourceDirectory] ["src"]
[::pom/build ::pom/resources ::pom/resource ::pom/directory] ["resources"]
[::pom/scm ::pom/tag] ["v1.2.3"]
[::pom/scm ::pom/url] ["https://github.com/example/p1"])
(is (= 2 (count (xml-path-val pom [::pom/dependencies]))))
(is (= 1 (count (xml-path-val pom [::pom/repositories]))))
;; check properties out
(is (.exists prop-out))
(is (submap? {"groupId" "test", "artifactId" "p1", "version" "1.2.3"} props)))))
(deftest test-update-existing-pom
(with-test-dir "test-data/p2"
(api/set-project-root! (.getAbsolutePath *test-dir*))
(api/delete {:path "target"})
(api/write-pom {:lib 'test/p2
:version "1.2.3"
:class-dir "target/classes"
:src-dirs ["src"]
:src-pom "pom.xml"
:resource-dirs ["resources"]
:basis (api/create-basis nil)
:scm {:tag "v1.2.3"
:url "https://github.com/example/p1"}})
(let [pom-dir (jio/file (project-path "target/classes/META-INF/maven/test/p2"))
pom-out (jio/file pom-dir "pom.xml")
pom (read-xml pom-out)
prop-out (jio/file pom-dir "pom.properties")
props (read-props prop-out)]
;; check xml out
(is (.exists pom-out))
(are [path val] (= val (xml-path-val pom path))
[::pom/packaging] ["jar"]
[::pom/groupId] ["test"]
[::pom/artifactId] ["p2"]
[::pom/version] ["1.2.3"]
[::pom/name] ["p2"]
[::pom/build ::pom/sourceDirectory] ["src"]
[::pom/build ::pom/resources ::pom/resource ::pom/directory] ["resources"]
[::pom/scm ::pom/tag] ["v1.2.3"])
(is (= 2 (count (xml-path-val pom [::pom/dependencies]))))
(is (= 1 (count (xml-path-val pom [::pom/repositories]))))
;; check properties out
(is (.exists prop-out))
(is (submap? {"groupId" "test", "artifactId" "p2", "version" "1.2.3"} props)))))
(deftest test-ignore-existing-pom
;; verify existing scm info is preserved if src-pom is supplied:
(with-test-dir "test-data/p4"
(api/set-project-root! (.getAbsolutePath *test-dir*))
(api/delete {:path "target"})
(api/write-pom {:lib 'test/p4
:version "1.2.3"
:class-dir "target/classes"
:src-dirs ["src"]
:src-pom "pom.xml"
:resource-dirs ["resources"]
:basis (api/create-basis nil)})
(let [pom-dir (jio/file (project-path "target/classes/META-INF/maven/test/p4"))
pom-out (jio/file pom-dir "pom.xml")
pom (read-xml pom-out)
prop-out (jio/file pom-dir "pom.properties")
props (read-props prop-out)]
;; check xml out
(is (.exists pom-out))
(are [path val] (= val (xml-path-val pom path))
[::pom/packaging] ["jar"]
[::pom/groupId] ["test"]
[::pom/artifactId] ["p4"]
[::pom/version] ["1.2.3"]
[::pom/name] ["p4"]
[::pom/build ::pom/sourceDirectory] ["src"]
[::pom/build ::pom/resources ::pom/resource ::pom/directory] ["resources"]
[::pom/scm ::pom/tag] ["HEAD"]
[::pom/scm ::pom/url] ["git@github.com:clojure/tools.build.git"])
(is (= 2 (count (xml-path-val pom [::pom/dependencies]))))
(is (= 1 (count (xml-path-val pom [::pom/repositories]))))
;; check properties out
(is (.exists prop-out))
(is (submap? {"groupId" "test", "artifactId" "p4", "version" "1.2.3"} props))))
;; verify no scm info is present if src-pom is :none:
(with-test-dir "test-data/p4"
(api/set-project-root! (.getAbsolutePath *test-dir*))
(api/delete {:path "target"})
(api/write-pom {:lib 'test/p4
:version "1.2.3"
:class-dir "target/classes"
:src-dirs ["src"]
:src-pom :none
:resource-dirs ["resources"]
:basis (api/create-basis nil)})
(let [pom-dir (jio/file (project-path "target/classes/META-INF/maven/test/p4"))
pom-out (jio/file pom-dir "pom.xml")
pom (read-xml pom-out)
prop-out (jio/file pom-dir "pom.properties")
props (read-props prop-out)]
;; check xml out
(is (.exists pom-out))
(are [path val] (= val (xml-path-val pom path))
[::pom/packaging] ["jar"]
[::pom/groupId] ["test"]
[::pom/artifactId] ["p4"]
[::pom/version] ["1.2.3"]
[::pom/name] ["p4"]
[::pom/build ::pom/sourceDirectory] ["src"]
[::pom/build ::pom/resources ::pom/resource ::pom/directory] ["resources"]
[::pom/scm ::pom/tag] nil
[::pom/scm ::pom/url] nil)
(is (= 2 (count (xml-path-val pom [::pom/dependencies]))))
(is (= 1 (count (xml-path-val pom [::pom/repositories]))))
;; check properties out
(is (.exists prop-out))
(is (submap? {"groupId" "test", "artifactId" "p4", "version" "1.2.3"} props)))))
;; check that optional deps are marked optional
(deftest test-optional
(with-test-dir "test-data/p3"
(api/set-project-root! (.getAbsolutePath *test-dir*))
(api/delete {:path "target"})
(api/write-pom {:lib 'test/p3
:version "1.2.3"
:class-dir "target/classes"
:src-dirs ["src"]
:src-pom "pom.xml"
:resource-dirs ["resources"]
:basis (api/create-basis nil)})
(let [pom-dir (jio/file (project-path "target/classes/META-INF/maven/test/p3"))
pom-out (jio/file pom-dir "pom.xml")]
(is (.exists pom-out))
(let [generated (slurp pom-out)]
(is (str/includes? generated "core.async"))
(is (str/includes? generated "true"))))))
;; check that supplying an empty repo map removes repos from generated pom
(deftest test-omit-repos
(with-test-dir "test-data/p1"
(api/set-project-root! (.getAbsolutePath *test-dir*))
(api/delete {:path "target"})
(api/write-pom {:lib 'test/p1
:version "1.2.3"
:class-dir "target/classes"
:src-dirs ["src"]
:src-pom "pom.xml"
:resource-dirs ["resources"]
:repos {} ;; replace repo map from deps.edn
:basis (api/create-basis nil)})
(let [pom-dir (jio/file (project-path "target/classes/META-INF/maven/test/p1"))
pom-out (jio/file pom-dir "pom.xml")]
(is (.exists pom-out))
(let [generated (slurp pom-out)]
(is (not (str/includes? generated "clojars")))))))
;; check that :extra-deps in basis aliases are reflected in pom deps
(deftest test-extra-deps
(with-test-dir "test-data/p1"
(api/set-project-root! (.getAbsolutePath *test-dir*))
(api/delete {:path "target"})
(api/write-pom {:lib 'test/p1
:version "1.2.3"
:class-dir "target/classes"
:src-dirs ["src"]
:src-pom "pom.xml"
:resource-dirs ["resources"]
:basis (api/create-basis '{:extra {:aliases {:ex {:extra-deps {org.clojure/data.json {:mvn/version "2.3.1"}}}}}
:aliases [:ex]})})
(let [pom-dir (jio/file (project-path "target/classes/META-INF/maven/test/p1"))
pom-out (jio/file pom-dir "pom.xml")]
(is (.exists pom-out))
(let [generated (slurp pom-out)]
(is (str/includes? generated "data.json"))))))
(deftest test-pom-path
(is (= (jio/file "META-INF/maven/a.b/c/pom.xml") (jio/file (api/pom-path {:lib 'a.b/c}))))
(let [prior api/*project-root*]
(api/set-project-root! ".")
(is (= (jio/file "./foo/META-INF/maven/a.b/c/pom.xml")
(jio/file (api/pom-path {:class-dir "foo" :lib 'a.b/c}))))
(api/set-project-root! prior)))
(deftest test-validate-lib
(with-test-dir "test-data/p1"
(api/set-project-root! (.getAbsolutePath *test-dir*))
(api/delete {:path "target"})
(try
(api/write-pom {:lib 'unqualified-lib ;; invalid
:version "1.2.3"
:class-dir "target/classes"
:src-dirs ["src"]
:src-pom "pom.xml"
:resource-dirs ["resources"]
:basis (api/create-basis nil)})
(catch ExceptionInfo e
(let [m (ex-message e)]
(is (str/includes? m ":lib"))
(is (str/includes? m "unqualified-lib"))
(is (str/includes? m "qualified-ident?")))))))
(deftest test-target
(with-test-dir "test-data/p1"
(api/set-project-root! (.getAbsolutePath *test-dir*))
(api/delete {:path "target"})
(api/write-pom {:lib 'test/p1
:version "1.2.3"
:target "target/output-pom.xml"
:src-dirs ["src"]
:resource-dirs ["resources"]
:basis (api/create-basis nil)})
(is (.exists (jio/file (project-path "target/output-pom.xml"))))))
(deftest test-target-or-class-dir
(with-test-dir "test-data/p1"
(api/set-project-root! (.getAbsolutePath *test-dir*))
(api/delete {:path "target"})
(try
(api/write-pom {:lib 'test/p1
:version "1.2.3"
;; NO :target or :class-dir
:src-dirs ["src"]
:src-pom "pom.xml"
:resource-dirs ["resources"]
:basis (api/create-basis nil)})
(is false)
(catch Throwable t
(is true)))))
(deftest test-pom-data
(with-test-dir "test-data/p1"
(api/set-project-root! (.getAbsolutePath *test-dir*))
(api/delete {:path "target"})
(api/write-pom {:lib 'test/p1
:version "1.2.3"
:target "target"
:src-dirs ["src"]
:resource-dirs ["resources"]
:basis (api/create-basis nil)
:pom-data [[:licenses
[:license
[:name "Apache-2.0"]
[:url "https://www.apache.org/licenses/LICENSE-2.0.txt"]
[:distribution "repo"]
[:comments "OSS license"]]]
[:foo
[:bar "hello"]]]})
(is (.exists (jio/file (project-path "target/pom.xml"))))
(let [written-pom (slurp (project-path "target/pom.xml"))]
(is (str/includes? written-pom "Apache-2.0"))
(is (str/includes? written-pom "hello")))))
(comment
(run-tests)
(test-validate-lib)
)
================================================
FILE: src/test/clojure/clojure/tools/build/tasks/test_process.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.tools.build.tasks.test-process
(:require
[clojure.test :refer :all]
[clojure.set :as set]
[clojure.tools.build.api :as api]
[clojure.tools.build.tasks.process :as process]))
(deftest test-large-process-output
(is (string? (api/git-process {:git-args ["log"]}))))
(deftest test-capture
(is (string? (:err (api/process {:command-args ["java" "-version"]
:err :capture})))))
(deftest test-env-and-capture
(when-not (#'process/windows?)
(is (= "hi\n"
(:out
(api/process {:env {"FOO" "hi"}
:command-args ["/bin/bash" "-c" "echo $FOO"]
:out :capture}))))))
(deftest test-need-cp-file
(let [f #'process/need-cp-file]
(are [os java length expected]
(= expected (f os java length))
"Windows 10" "9.0.1" 10000 true
"Windows 10" "9.0.1" 5000 false
"Windows 10" "1.8.0_261" 10000 false
"Mac OS X" "17" 10000 false)))
(deftest test-java-process-uses-and-merges-basis-jvm-opts
(let [basis (api/create-basis {:extra {:aliases {:opts {:jvm-opts ["-Dhi=there"]}}}
:aliases [:opts]})
command (api/java-command {:basis basis, :main 'clojure.main, :java-opts ["-Dfoo=bar"]})]
(is (set/subset? #{"-Dhi=there" "-Dfoo=bar"} (set (:command-args command))))))
================================================
FILE: src/test/clojure/clojure/tools/build/tasks/test_uber.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.tools.build.tasks.test-uber
(:require
[clojure.set :as set]
[clojure.string :as str]
[clojure.test :refer :all]
[clojure.java.io :as jio]
[clojure.tools.build.api :as api]
[clojure.tools.build.tasks.uber :as uber]
[clojure.tools.build.test-util :refer :all]
[clojure.tools.build.util.zip :as zip]
[clojure.tools.build.tasks.test-jar :as test-jar])
(:import
[clojure.lang ExceptionInfo]
[java.io ByteArrayInputStream]))
(defn- string->stream
[^String s]
(ByteArrayInputStream. (.getBytes s "UTF-8")))
(deftest string-stream-rt
(are [s] (= s (#'uber/stream->string (string->stream s)))
""
"abc"))
(deftest test-uber
(let [uber-path "target/p1-uber.jar"]
(with-test-dir "test-data/p1"
(api/set-project-root! (.getAbsolutePath *test-dir*))
(api/javac {:class-dir "target/classes"
:src-dirs ["java"]})
(api/copy-dir {:target-dir "target/classes"
:src-dirs ["src"]})
(api/uber {:class-dir "target/classes"
:basis (api/create-basis nil)
:uber-file uber-path
:main 'foo.bar})
(let [uf (jio/file (project-path uber-path))]
(is (true? (.exists uf)))
(is (set/subset?
#{"META-INF/MANIFEST.MF" "foo/" "foo/bar.clj" "foo/Demo2.class" "foo/Demo1.class"}
(set (map :name (zip/list-zip (project-path uber-path))))))
(is (str/includes? (test-jar/slurp-manifest uf) "Main-Class: foo.bar"))))))
(deftest test-custom-manifest
(let [uber-path "target/p1-uber.jar"]
(with-test-dir "test-data/p1"
(api/set-project-root! (.getAbsolutePath *test-dir*))
(api/javac {:class-dir "target/classes"
:src-dirs ["java"]})
(api/copy-dir {:target-dir "target/classes"
:src-dirs ["src"]})
(api/uber {:class-dir "target/classes"
:uber-file uber-path
:main 'foo.bar
:manifest {"Main-Class" "baz" ;; overrides :main
'Custom-Thing 100}}) ;; stringify kvs
(let [uf (jio/file (project-path uber-path))]
(is (true? (.exists uf)))
(is (= #{"META-INF/MANIFEST.MF" "foo/" "foo/bar.clj" "foo/Demo2.class" "foo/Demo1.class"}
(set (map :name (zip/list-zip (project-path uber-path))))))
(let [manifest-out (test-jar/slurp-manifest uf)]
(is (str/includes? manifest-out "Main-Class: baz"))
(is (str/includes? manifest-out "Custom-Thing: 100")))))))
(deftest test-conflicts
(with-test-dir "test-data/uber-conflict"
(api/set-project-root! (.getAbsolutePath *test-dir*))
;; make "jars"
(doseq [j ["j1" "j2" "j3"]]
(let [classes (format "target/%s/classes" j)
jar-file (format "target/%s.jar" j)]
(api/copy-dir {:target-dir classes :src-dirs [j]})
(api/jar {:class-dir classes :jar-file jar-file})))
;; uber including j1, j2, j3
(api/uber {:class-dir "target/classes"
:basis (api/create-basis {:root nil
:project {:deps {'dummy/j1 {:local/root "target/j1.jar"}
'dummy/j2 {:local/root "target/j2.jar"}
'dummy/j3 {:local/root "target/j3.jar"}}}})
:uber-file "target/conflict.jar"
:conflict-handlers {"ignore.txt" :ignore
"overwrite.txt" :overwrite
"append.txt" :append}})
;; unzip
(api/unzip {:zip-file "target/conflict.jar" :target-dir "target/unzip"})
;; non-conflicting files are combined, conflicting files are reconciled
(let [fs (map :name (zip/list-zip (project-path "target/conflict.jar")))]
(is
(set/subset?
#{"META-INF/LICENSE.txt" "META-INF/MANIFEST.MF"
"data_readers.clj"
"my/j1.txt" "my/j2.txt"
"ignore.txt" "overwrite.txt" "append.txt"}
(set fs))))
;; data_readers.clj merge
(is (= '{j1a my.foo/j1a-reader, j1b my.bar/j1b-reader,
j2a my.foo/j2a-reader, j2b my.bar/j2b-reader}
(read-string (slurp (project-path "target/unzip/data_readers.clj")))))
;; data_readers.cljc merge
(is (= {'j1a (reader-conditional '(:cljs my.cljs.foo/j1a-reader :clj my.clj.foo/j1a-reader) false)
'j1b (reader-conditional '(:cljs my.cljs.foo/j1b-reader :clj my.clj.foo/j1b-reader) false)
'j2a (reader-conditional '(:cljs my.cljs.foo/j2a-reader :clj my.clj.foo/j2a-reader) false)
'j2b (reader-conditional '(:cljs my.cljs.foo/j2b-reader :clj my.clj.foo/j2b-reader) false)}
(read-string {:read-cond :preserve :features #{:clj}}
(slurp (project-path "target/unzip/data_readers.cljc")))))
;; ignore files ignore, so first one wins
(is (= (slurp (project-path "j1/ignore.txt"))
(slurp (project-path "target/unzip/ignore.txt"))))
;; overwrite files overwrite, so last wins
(is (= (slurp (project-path "j2/overwrite.txt"))
(slurp (project-path "target/unzip/overwrite.txt"))))
;; append files append
(is (= (str (slurp (project-path "j1/append.txt")) "\n"
(slurp (project-path "j2/append.txt")))
(slurp (project-path "target/unzip/append.txt"))))
;; LICENSE files append but no dupes - include j1 and j2, but not j3 (dupe of j1)
(is (= (str (slurp (project-path "j1/META-INF/LICENSE.txt")) "\n"
(slurp (project-path "j2/META-INF/LICENSE.txt")))
(slurp (project-path "target/unzip/META-INF/LICENSE.txt"))))))
(deftest test-conflicts-but-files
(with-test-dir "test-data/uber-conflict"
(api/set-project-root! (.getAbsolutePath *test-dir*))
;; make dirs
(doseq [j ["j1" "j2" "j3"]]
(let [classes (format "target/%s/classes" j)]
(api/copy-dir {:target-dir classes :src-dirs [j]})
(spit (project-path (format "target/%s/classes/deps.edn" j)) "{:paths [\".\"]}")))
;; uber including j1, j2, j3 as local dirs
(api/uber {:class-dir "target/classes"
:basis (api/create-basis {:root nil
:project {:deps {'dummy/j1 {:local/root "target/j1/classes"}
'dummy/j2 {:local/root "target/j2/classes"}
'dummy/j3 {:local/root "target/j3/classes"}}}})
:uber-file "target/conflict.jar"
:conflict-handlers {"ignore.txt" :ignore
"overwrite.txt" :overwrite
"append.txt" :append}})
;; unzip
(api/unzip {:zip-file "target/conflict.jar" :target-dir "target/unzip"})
;; non-conflicting files are combined, conflicting files are reconciled
(let [fs (map :name (zip/list-zip (project-path "target/conflict.jar")))]
(is
(set/subset?
#{"META-INF/LICENSE.txt" "META-INF/MANIFEST.MF"
"data_readers.clj"
"my/j1.txt" "my/j2.txt"
"ignore.txt" "overwrite.txt" "append.txt"}
(set fs))))
;; data_readers.clj merge
(is (= '{j1a my.foo/j1a-reader, j1b my.bar/j1b-reader,
j2a my.foo/j2a-reader, j2b my.bar/j2b-reader}
(read-string (slurp (project-path "target/unzip/data_readers.clj")))))
;; data_readers.cljc merge
(is (= {'j1a (reader-conditional '(:cljs my.cljs.foo/j1a-reader :clj my.clj.foo/j1a-reader) false)
'j1b (reader-conditional '(:cljs my.cljs.foo/j1b-reader :clj my.clj.foo/j1b-reader) false)
'j2a (reader-conditional '(:cljs my.cljs.foo/j2a-reader :clj my.clj.foo/j2a-reader) false)
'j2b (reader-conditional '(:cljs my.cljs.foo/j2b-reader :clj my.clj.foo/j2b-reader) false)}
(read-string {:read-cond :preserve :features #{:clj}}
(slurp (project-path "target/unzip/data_readers.cljc")))))
;; ignore files ignore, so first one wins
(is (= (slurp (project-path "j1/ignore.txt"))
(slurp (project-path "target/unzip/ignore.txt"))))
;; overwrite files overwrite, so last wins
(is (= (slurp (project-path "j2/overwrite.txt"))
(slurp (project-path "target/unzip/overwrite.txt"))))
;; append files append
(is (= (str (slurp (project-path "j1/append.txt")) "\n"
(slurp (project-path "j2/append.txt")))
(slurp (project-path "target/unzip/append.txt"))))
;; LICENSE files append but no dupes - include j1 and j2, but not j3 (dupe of j1)
(is (= (str (slurp (project-path "j1/META-INF/LICENSE.txt")) "\n"
(slurp (project-path "j2/META-INF/LICENSE.txt")))
(slurp (project-path "target/unzip/META-INF/LICENSE.txt"))))))
(deftest test-case-sensitive-dir-file-collision
(with-test-dir "test-data/case-sensitive-collision"
(api/set-project-root! (.getAbsolutePath *test-dir*))
;; make "jars"
(doseq [j ["j1" "j2"]]
(let [classes (format "target/%s/classes" j)
jar-file (format "target/%s.jar" j)]
(api/copy-dir {:target-dir classes :src-dirs [j]})
(api/jar {:class-dir classes :jar-file jar-file})))
;; uber including j1, j2 - should fail with conflict
(let [basis (api/create-basis {:root nil
:project {:deps {'dummy/j1 {:local/root "target/j1.jar"}
'dummy/j2 {:local/root "target/j2.jar"}}}})]
(try
(api/uber {:class-dir "target/classes", :basis basis, :uber-file "target/collision.jar"})
(catch ExceptionInfo ex
(= "Cannot write foo/hi.txt from dummy/j2 as parent dir is a file from another lib. One of them must be excluded."
(ex-message ex))))
;; uber including j1, j2 but excluding one of the conflicts
(api/uber {:class-dir "target/classes", :basis basis, :uber-file "target/collision.jar"
:exclude ["FOO"]})
;; after exclusion, only foo/hi.txt
(let [fs (map :name (zip/list-zip (project-path "target/collision.jar")))]
(is (= #{"META-INF/MANIFEST.MF" "foo/" "foo/hi.txt"} (set fs)))))))
;; TBUILD-35 - it is rare but possible for a jar to contain a / dir entry
(deftest test-bad-zip-with-root-dir
(with-test-dir "test-data/bad-zip"
(api/set-project-root! (.getAbsolutePath *test-dir*))
(let [basis (api/create-basis {:root nil})]
(api/uber {:class-dir "target/classes" :basis basis, :uber-file "target/out.jar"})
(.exists (jio/file (api/resolve-path "target/out.jar"))))))
(comment
(test-conflicts)
(test-conflicts-but-files)
(test-case-sensitive-dir-file-collision)
(run-tests)
)
================================================
FILE: src/test/clojure/clojure/tools/build/tasks/test_write_file.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.tools.build.tasks.test-write-file
(:require
[clojure.test :refer :all :as test]
[clojure.edn :as edn]
[clojure.java.io :as jio]
[clojure.tools.build.api :as api]
[clojure.tools.build.test-util :refer :all]))
(deftest test-touch
(with-test-dir "test-data/p1"
(api/set-project-root! (.getAbsolutePath *test-dir*))
(api/write-file {:path "target/out.txt"})
(is (.exists (jio/file (project-path "target/out.txt"))))))
(deftest test-write-data
(with-test-dir "test-data/p1"
(let [data {:abc "abc" :def [1 2 3]}]
(api/set-project-root! (.getAbsolutePath *test-dir*))
(api/write-file {:path "target/out.txt"
:content data})
(let [out-file (jio/file (project-path "target/out.txt"))]
(is (.exists out-file))
(is (= data (edn/read-string (slurp out-file))))))))
(deftest test-write-string
(with-test-dir "test-data/p1"
(let [string "abcd/nef/n"]
(api/set-project-root! (.getAbsolutePath *test-dir*))
(api/write-file {:path "target/out.txt"
:string string})
(let [out-file (jio/file (project-path "target/out.txt"))]
(is (.exists out-file))
(is (= string (slurp out-file)))))))
(comment
(run-tests)
)
================================================
FILE: src/test/clojure/clojure/tools/build/tasks/test_zip.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.tools.build.tasks.test-zip
(:require
[clojure.test :refer :all :as test]
[clojure.java.io :as jio]
[clojure.tools.build.api :as api]
[clojure.tools.build.test-util :refer :all]
[clojure.tools.build.util.zip :as zip]
[clojure.tools.build.util.file :as file])
(:import
[java.io File]
[java.nio.file Files LinkOption]
[java.util.concurrent TimeUnit]))
(deftest test-zip
(let [zip-path "target/output.zip"]
(with-test-dir "test-data/p1"
(api/set-project-root! (.getAbsolutePath *test-dir*))
(api/zip {:src-dirs ["src"]
:zip-file zip-path})
(is (true? (.exists (jio/file (project-path zip-path)))))
(is (= #{"foo/" "foo/bar.clj"}
(set (map :name (zip/list-zip (project-path zip-path)))))))))
(deftest test-unzip
(let [zip-path "target/output.zip"]
(with-test-dir "test-data/p1"
(api/set-project-root! (.getAbsolutePath *test-dir*))
(api/zip {:src-dirs ["src"]
:zip-file zip-path})
(api/unzip {:zip-file zip-path :target-dir "target/unzipped"})
(let [original (file/collect-files (jio/file (project-path "src")) )
roundtrip (file/collect-files (jio/file (project-path "target/unzipped")))]
(is (= 1 (count original) (count roundtrip)))
(let [f1 ^File (first original)
f2 ^File (first roundtrip)]
(is (= (.getName f1) (.getName f2)))
(is (= (.length f1) (.length f2)))
(is (= (.to (Files/getLastModifiedTime (.toPath f1) (into-array LinkOption [])) TimeUnit/SECONDS)
(.to (Files/getLastModifiedTime (.toPath f2) (into-array LinkOption [])) TimeUnit/SECONDS))))))))
(comment
(run-tests)
)
================================================
FILE: src/test/clojure/clojure/tools/build/test_project_root.clj
================================================
(ns clojure.tools.build.test-project-root
(:require
[clojure.java.io :as jio]
[clojure.test :refer :all]
[clojure.tools.build.api :as api])
(:import
[java.nio.file Files]
[java.nio.file.attribute FileAttribute]))
(deftest test-with-project-root
(let [path (Files/createTempDirectory "p" (make-array FileAttribute 0))
root-dir (jio/file (.toString path))
p-dir (jio/file root-dir "p")
deps (jio/file p-dir "deps.edn")]
(jio/make-parents deps)
(spit deps "{:deps {org.clojure/data.json {:mvn/version \"2.3.0\"}}}")
(api/set-project-root! (.toString path))
(api/with-project-root (.getPath (jio/file (.toString path) "p"))
(is (contains? (-> (api/create-basis) :libs keys set) 'org.clojure/data.json)))))
(comment
(test-with-project-root)
)
================================================
FILE: src/test/clojure/clojure/tools/build/test_util.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.tools.build.test-util
(:require
[clojure.java.io :as jio]
[clojure.string :as str]
[clojure.test :as test]
[clojure.tools.build.util.file :as file])
(:import
[java.io File]))
(def ^:dynamic ^File *test-dir* nil)
(defmacro with-test-dir
[test-project & body]
`(let [name# (-> test/*testing-vars* last symbol str)
dir# (jio/file "test-out" name#)]
(file/delete dir#)
(.mkdirs dir#)
(file/copy-contents (jio/file ~test-project) dir#)
(binding [*test-dir* dir#]
~@body)))
(defn project-path [& parts]
(str/join "/" (cons (.getAbsolutePath *test-dir*) parts)))
(defn submap?
"Is m1 a subset of m2?"
[m1 m2]
(if (and (map? m1) (map? m2))
(every? (fn [[k v]] (and (contains? m2 k)
(submap? v (get m2 k))))
m1)
(= m1 m2)))
(def windows? (str/starts-with? (System/getProperty "os.name") "Windows"))
================================================
FILE: test-data/assert/deps.edn
================================================
{:paths ["target/classes" "src"]
:deps
{org.clojure/clojure {:mvn/version "1.10.3"}}}
================================================
FILE: test-data/assert/src/foo/check_assert.clj
================================================
(ns foo.check-assert)
(defn f
[x]
{:pre [(keyword? x)]}
x)
================================================
FILE: test-data/bad-zip/deps.edn
================================================
{:deps {my/bad {:local/root "bad.jar"}}}
================================================
FILE: test-data/case-sensitive-collision/j1/FOO
================================================
I'm a foo
================================================
FILE: test-data/case-sensitive-collision/j2/foo/hi.txt
================================================
hi
================================================
FILE: test-data/nses/src/a.clj
================================================
(ns a
(:require b))
================================================
FILE: test-data/nses/src/b.clj
================================================
(ns b
(:require c))
================================================
FILE: test-data/nses/src/c.clj
================================================
(ns c)
================================================
FILE: test-data/nses/src/d.clj
================================================
(ns d)
================================================
FILE: test-data/p1/deps.edn
================================================
{:paths [:clj-paths :resource-paths]
:deps
{org.clojure/clojure {:mvn/version "1.10.1"}
org.clojure/core.cache {:mvn/version "1.0.207"}}
:aliases
{:clj-paths ["src"]
:resource-paths ["resources"]}}
================================================
FILE: test-data/p1/java/foo/Demo1.java
================================================
package foo;
public class Demo1 {
public static void main(String[] args) {
System.out.println("Hello");
}
}
================================================
FILE: test-data/p1/java/foo/Demo2.java
================================================
package foo;
public class Demo2 {
public static void main(String[] args) {
System.out.println("Hello");
}
}
================================================
FILE: test-data/p1/resources/data.edn
================================================
{:version "$version"}
================================================
FILE: test-data/p1/src/foo/bar.clj
================================================
(ns foo.bar
"__REPLACE__"
(:gen-class))
(defn hello [] (println "hello"))
(defn -main
[& args]
(hello))
================================================
FILE: test-data/p2/deps.edn
================================================
{:paths [:clj-paths :resource-paths]
:deps
{org.clojure/clojure {:mvn/version "1.10.1"}
org.clojure/core.cache {:mvn/version "1.0.207"}}
:aliases
{:clj-paths ["src"]
:resource-paths ["resources"]}}
================================================
FILE: test-data/p2/pom.xml
================================================
4.0.0jarfoobar0.0.1name
================================================
FILE: test-data/p2/resources/data.edn
================================================
{:version "$version"}
================================================
FILE: test-data/p2/src/foo/bar.clj
================================================
(ns foo.bar)
(defn hello [] (println "hello"))
================================================
FILE: test-data/p3/deps.edn
================================================
{:paths [:clj-paths :resource-paths]
:deps
{org.clojure/clojure {:mvn/version "1.10.1"}
org.clojure/core.async {:mvn/version "1.1.587" :optional true}}
:aliases
{:clj-paths ["src"]
:resource-paths ["resources"]}}
================================================
FILE: test-data/p3/resources/data.edn
================================================
{:version "$version"}
================================================
FILE: test-data/p3/src/foo/bar.clj
================================================
(ns foo.bar)
(defn hello [] (println "hello"))
================================================
FILE: test-data/p4/deps.edn
================================================
{:paths [:clj-paths :resource-paths]
:deps
{org.clojure/clojure {:mvn/version "1.10.1"}
org.clojure/core.cache {:mvn/version "1.0.207"}}
:aliases
{:clj-paths ["src"]
:resource-paths ["resources"]}}
================================================
FILE: test-data/p4/pom.xml
================================================
4.0.0jarfoobar0.0.1namescm:git:git@github.com:clojure/tools.build.gitscm:git:git@github.com:clojure/tools.build.gitgit@github.com:clojure/tools.build.gitHEAD
================================================
FILE: test-data/p4/resources/data.edn
================================================
{:version "$version"}
================================================
FILE: test-data/p4/src/foo/bar.clj
================================================
(ns foo.bar)
(defn hello [] (println "hello"))
================================================
FILE: test-data/reflecting/deps.edn
================================================
{:paths ["src"]
:deps
{org.clojure/clojure {:mvn/version "1.12.0"}}
}
================================================
FILE: test-data/reflecting/src/foo/bar.clj
================================================
(ns foo.bar
(:gen-class))
(defn foo [s] (.length s))
(defn -main [& args]
(println (foo "abc")))
================================================
FILE: test-data/uber-conflict/j1/META-INF/LICENSE.txt
================================================
this is a dummy license
that will be appended
================================================
FILE: test-data/uber-conflict/j1/append.txt
================================================
j1
================================================
FILE: test-data/uber-conflict/j1/data_readers.clj
================================================
{j1a my.foo/j1a-reader
j1b my.bar/j1b-reader}
================================================
FILE: test-data/uber-conflict/j1/data_readers.cljc
================================================
{j1a #?(:cljs my.cljs.foo/j1a-reader :clj my.clj.foo/j1a-reader)
j1b #?(:cljs my.cljs.foo/j1b-reader :clj my.clj.foo/j1b-reader)}
================================================
FILE: test-data/uber-conflict/j1/ignore.txt
================================================
j1 file
================================================
FILE: test-data/uber-conflict/j1/my/j1.txt
================================================
j1 file
================================================
FILE: test-data/uber-conflict/j1/overwrite.txt
================================================
j1 file
================================================
FILE: test-data/uber-conflict/j2/META-INF/LICENSE.txt
================================================
this is a different license
so should be included
================================================
FILE: test-data/uber-conflict/j2/append.txt
================================================
j2
================================================
FILE: test-data/uber-conflict/j2/data_readers.clj
================================================
{j2a my.foo/j2a-reader
j2b my.bar/j2b-reader}
================================================
FILE: test-data/uber-conflict/j2/data_readers.cljc
================================================
{j2a #?(:cljs my.cljs.foo/j2a-reader :clj my.clj.foo/j2a-reader)
j2b #?(:cljs my.cljs.foo/j2b-reader :clj my.clj.foo/j2b-reader)}
================================================
FILE: test-data/uber-conflict/j2/ignore.txt
================================================
j2 file
================================================
FILE: test-data/uber-conflict/j2/my/j2.txt
================================================
j2 file
================================================
FILE: test-data/uber-conflict/j2/overwrite.txt
================================================
j2 file
================================================
FILE: test-data/uber-conflict/j3/META-INF/LICENSE.txt
================================================
this is a dummy license
that will be appended