This is an automated email from the ASF dual-hosted git repository.
bodewig pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ant.git
The following commit(s) were added to refs/heads/master by this push:
new ebf0fa0ed add canCreateSymlink condition
ebf0fa0ed is described below
commit ebf0fa0ed675070a191c5e7fe9869fe106f29ddb
Author: Stefan Bodewig <[email protected]>
AuthorDate: Sat Feb 7 14:16:49 2026 +0100
add canCreateSymlink condition
---
WHATSNEW | 3 ++
manual/Tasks/conditions.html | 5 ++
src/main/org/apache/tools/ant/Diagnostics.java | 16 ++++++
src/main/org/apache/tools/ant/antlib.xml | 2 +
.../taskdefs/condition/CanCreateSymbolicLink.java | 52 +++++++++++++++++++
.../apache/tools/ant/types/conditions/antlib.xml | 2 +
.../antunit/core/dirscanner-symlinks-test.xml | 60 +++++++++++-----------
.../taskdefs/optional/windows/mklink-test.xml | 10 +++-
8 files changed, 118 insertions(+), 32 deletions(-)
diff --git a/WHATSNEW b/WHATSNEW
index b2d6b4633..fac59d316 100644
--- a/WHATSNEW
+++ b/WHATSNEW
@@ -68,6 +68,9 @@ Other changes:
<symlink action="delete" ...> does - it has been introduced to
handle symlinks and junctions via a single API.
+ * added <canCreateSymlink> condition that evaluates to true if the
+ current Ant process can create symbolic links.
+
Changes from Ant 1.10.14 TO Ant 1.10.15
=======================================
diff --git a/manual/Tasks/conditions.html b/manual/Tasks/conditions.html
index 3e09e3bb5..0446aca97 100644
--- a/manual/Tasks/conditions.html
+++ b/manual/Tasks/conditions.html
@@ -974,5 +974,10 @@ <h4 id="javaversion">javaversion</h4>
<p>will evaluate to true if the current JVM is Java 9 or above.</p>
+<h4 id="canCreateSymlink">canCreateSymlink</h4>
+<p><em>since Ant 1.10.16</em></p>
+<p>Evaluates to true if the current process is able to create symbolic
+ links.</p>
+
</body>
</html>
diff --git a/src/main/org/apache/tools/ant/Diagnostics.java
b/src/main/org/apache/tools/ant/Diagnostics.java
index 9333a576a..0607716a7 100644
--- a/src/main/org/apache/tools/ant/Diagnostics.java
+++ b/src/main/org/apache/tools/ant/Diagnostics.java
@@ -41,6 +41,7 @@ import org.apache.tools.ant.util.JAXPUtils;
import org.apache.tools.ant.util.JavaEnvUtils;
import org.apache.tools.ant.util.ProxySetup;
import org.apache.tools.ant.util.java15.ProxyDiagnostics;
+import org.apache.tools.ant.taskdefs.condition.CanCreateSymbolicLink;
import org.xml.sax.XMLReader;
/**
@@ -320,6 +321,9 @@ public final class Diagnostics {
header(out, "XSLT Processor information");
doReportXSLTProcessorInfo(out);
+ header(out, "Symlinking information");
+ doReportSymlinkInfo(out);
+
header(out, "System properties");
doReportSystemProperties(out);
@@ -682,4 +686,16 @@ public final class Diagnostics {
out.println(proxyDiag.toString());
}
+ /**
+ * Report abilities around symbolic links.
+ * @param out the stream to print the content to
+ */
+ private static void doReportSymlinkInfo(PrintStream out) {
+ Project p = new Project();
+ p.initProperties();
+ CanCreateSymbolicLink c = new CanCreateSymbolicLink();
+ c.setProject(p);
+ out.println("can create symbolic links: " + c.eval());
+ }
+
}
diff --git a/src/main/org/apache/tools/ant/antlib.xml
b/src/main/org/apache/tools/ant/antlib.xml
index 68bdab097..6b33e30e5 100644
--- a/src/main/org/apache/tools/ant/antlib.xml
+++ b/src/main/org/apache/tools/ant/antlib.xml
@@ -35,6 +35,8 @@
<!-- conditions -->
<componentdef name="and" onerror="ignore"
classname="org.apache.tools.ant.taskdefs.condition.And"/>
+ <componentdef name="canCreateSymlink" onerror="ignore"
+
classname="org.apache.tools.ant.taskdefs.condition.CanCreateSymbolicLink"/>
<componentdef name="contains" onerror="ignore"
classname="org.apache.tools.ant.taskdefs.condition.Contains"/>
<componentdef name="equals" onerror="ignore"
diff --git
a/src/main/org/apache/tools/ant/taskdefs/condition/CanCreateSymbolicLink.java
b/src/main/org/apache/tools/ant/taskdefs/condition/CanCreateSymbolicLink.java
new file mode 100644
index 000000000..41b035815
--- /dev/null
+++
b/src/main/org/apache/tools/ant/taskdefs/condition/CanCreateSymbolicLink.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.condition;
+
+import java.io.File;
+import java.nio.file.Files;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Tests whether the current process can create a symbolic link.
+ *
+ * <p>Actually tries to create a temporary symbolic link so is completely
independent of the current platform.</p>
+ *
+ * @since Ant 1.10.16
+ */
+public class CanCreateSymbolicLink extends ProjectComponent implements
Condition {
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ @Override
+ public boolean eval() {
+ File linkTarget = FILE_UTILS.createTempFile(getProject(), null, null,
null, true, true);
+ File link = FILE_UTILS.createTempFile(getProject(), null, null, null,
false, false);
+ try {
+ Files.createSymbolicLink(link.toPath(), linkTarget.toPath());
+ } catch (Exception ex) {
+ log("Cannot create symbolic links, caught " + ex.getClass()
+ + " exception with message " + ex.getMessage(),
+ Project.MSG_VERBOSE);
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/main/org/apache/tools/ant/types/conditions/antlib.xml
b/src/main/org/apache/tools/ant/types/conditions/antlib.xml
index 6a6458245..f970cf43a 100644
--- a/src/main/org/apache/tools/ant/types/conditions/antlib.xml
+++ b/src/main/org/apache/tools/ant/types/conditions/antlib.xml
@@ -38,6 +38,8 @@
classname="org.apache.tools.ant.taskdefs.condition.And"/>
<typedef name="antversion" onerror="ignore"
classname="org.apache.tools.ant.taskdefs.condition.AntVersion"/>
+ <typedef name="canCreateSymlink" onerror="ignore"
+
classname="org.apache.tools.ant.taskdefs.condition.CanCreateSymbolicLink"/>
<typedef name="contains" onerror="ignore"
classname="org.apache.tools.ant.taskdefs.condition.Contains"/>
<typedef name="equals" onerror="ignore"
diff --git a/src/tests/antunit/core/dirscanner-symlinks-test.xml
b/src/tests/antunit/core/dirscanner-symlinks-test.xml
index 8e365cd6d..3e0ecb910 100644
--- a/src/tests/antunit/core/dirscanner-symlinks-test.xml
+++ b/src/tests/antunit/core/dirscanner-symlinks-test.xml
@@ -24,8 +24,8 @@
<mkdir dir="${base}"/>
</target>
- <target name="checkOs">
- <condition property="unix"><os family="unix"/></condition>
+ <target name="checkCanCreateSymlink">
+ <condition property="canCreateSymlink"><canCreateSymlink/></condition>
</target>
<macrodef name="assertDirIsEmpty">
@@ -40,8 +40,8 @@
</macrodef>
<target name="testSymlinkToSiblingFollow"
- depends="checkOs, setUp, -sibling"
- if="unix">
+ depends="checkCanCreateSymlink, setUp, -sibling"
+ if="canCreateSymlink">
<copy todir="${output}">
<fileset dir="${base}" followsymlinks="true"/>
</copy>
@@ -49,8 +49,8 @@
</target>
<target name="testSymlinkToSiblingNoFollow"
- depends="checkOs, setUp, -sibling"
- if="unix">
+ depends="checkCanCreateSymlink, setUp, -sibling"
+ if="canCreateSymlink">
<copy todir="${output}">
<fileset dir="${base}" followsymlinks="false"/>
</copy>
@@ -58,8 +58,8 @@
</target>
<target name="testBasedirIsSymlinkFollow"
- depends="checkOs, setUp, -basedir-as-symlink"
- if="unix">
+ depends="checkCanCreateSymlink, setUp, -basedir-as-symlink"
+ if="canCreateSymlink">
<copy todir="${output}">
<fileset dir="${base}" followsymlinks="true"/>
</copy>
@@ -67,8 +67,8 @@
</target>
<target name="testBasedirIsSymlinkNoFollow"
- depends="checkOs, setUp, -basedir-as-symlink"
- if="unix">
+ depends="checkCanCreateSymlink, setUp, -basedir-as-symlink"
+ if="canCreateSymlink">
<copy todir="${output}">
<fileset dir="${base}" followsymlinks="false"/>
</copy>
@@ -76,8 +76,8 @@
</target>
<target name="testLinkToParentFollow"
- depends="checkOs, setUp, -link-to-parent"
- if="unix">
+ depends="checkCanCreateSymlink, setUp, -link-to-parent"
+ if="canCreateSymlink">
<copy todir="${output}">
<fileset dir="${base}" followsymlinks="true" maxLevelsOfSymlinks="1"/>
</copy>
@@ -87,8 +87,8 @@
</target>
<target name="testLinkToParentFollowMax2"
- depends="checkOs, setUp, -link-to-parent"
- if="unix">
+ depends="checkCanCreateSymlink, setUp, -link-to-parent"
+ if="canCreateSymlink">
<copy todir="${output}">
<fileset dir="${base}" followsymlinks="true" maxLevelsOfSymlinks="2"/>
</copy>
@@ -98,8 +98,8 @@
</target>
<target name="testLinkToParentFollowWithInclude"
- depends="checkOs, setUp, -link-to-parent"
- if="unix">
+ depends="checkCanCreateSymlink, setUp, -link-to-parent"
+ if="canCreateSymlink">
<copy todir="${output}">
<fileset dir="${base}" followsymlinks="true">
<include name="A/B/*"/>
@@ -111,8 +111,8 @@
<!-- supposed to fail? -->
<target name="testLinkToParentFollowWithIncludeMultiFollow"
- depends="checkOs, setUp, -link-to-parent"
- if="unix">
+ depends="checkCanCreateSymlink, setUp, -link-to-parent"
+ if="canCreateSymlink">
<copy todir="${output}">
<fileset dir="${base}" followsymlinks="true">
<include name="A/base/A/B/*"/>
@@ -123,8 +123,8 @@
</target>
<target name="testLinkToParentNoFollow"
- depends="checkOs, setUp, -link-to-parent"
- if="unix">
+ depends="checkCanCreateSymlink, setUp, -link-to-parent"
+ if="canCreateSymlink">
<copy todir="${output}">
<fileset dir="${base}" followsymlinks="false"/>
</copy>
@@ -133,8 +133,8 @@
</target>
<target name="testSillyLoopFollow"
- depends="checkOs, setUp, -silly-loop"
- if="unix">
+ depends="checkCanCreateSymlink, setUp, -silly-loop"
+ if="canCreateSymlink">
<copy todir="${output}">
<fileset dir="${base}" followsymlinks="true"/>
</copy>
@@ -143,8 +143,8 @@
</target>
<target name="testSillyLoopNoFollow"
- depends="checkOs, setUp, -silly-loop"
- if="unix">
+ depends="checkCanCreateSymlink, setUp, -silly-loop"
+ if="canCreateSymlink">
<copy todir="${output}">
<fileset dir="${base}" followsymlinks="false"/>
</copy>
@@ -163,8 +163,8 @@
</target>
<target name="testRepeatedNameWithLinkButNoLoop"
- depends="checkOs, setUp"
- if="unix">
+ depends="checkCanCreateSymlink, setUp"
+ if="canCreateSymlink">
<mkdir dir="${base}/A/A/A/B"/>
<touch file="${base}/A/A/A/B/file.txt"/>
<symlink link="${base}/A/A/A/A" resource="${base}/A/A/A/B"/>
@@ -174,26 +174,26 @@
<au:assertFileExists file="${output}/A/A/A/A/file.txt"/>
</target>
- <target name="-sibling" if="unix">
+ <target name="-sibling" if="canCreateSymlink">
<mkdir dir="${base}/A"/>
<touch file="${base}/A/file.txt"/>
<symlink link="${base}/B" resource="${base}/A"/>
</target>
- <target name="-basedir-as-symlink" if="unix">
+ <target name="-basedir-as-symlink" if="canCreateSymlink">
<delete dir="${base}"/>
<mkdir dir="${input}/realdir"/>
<touch file="${input}/realdir/file.txt"/>
<symlink link="${base}" resource="${input}/realdir"/>
</target>
- <target name="-link-to-parent" if="unix">
+ <target name="-link-to-parent" if="canCreateSymlink">
<mkdir dir="${input}/B"/>
<touch file="${input}/B/file.txt"/>
<symlink link="${base}/A" resource="${input}"/>
</target>
- <target name="-silly-loop" if="unix">
+ <target name="-silly-loop" if="canCreateSymlink">
<delete dir="${base}"/>
<symlink link="${base}" resource="${input}"/>
</target>
diff --git a/src/tests/antunit/taskdefs/optional/windows/mklink-test.xml
b/src/tests/antunit/taskdefs/optional/windows/mklink-test.xml
index 7f5d4b0df..634df7e85 100644
--- a/src/tests/antunit/taskdefs/optional/windows/mklink-test.xml
+++ b/src/tests/antunit/taskdefs/optional/windows/mklink-test.xml
@@ -23,6 +23,12 @@
<condition property="isWindows">
<os family="windows" />
</condition>
+ <condition property="canCreateSymlink">
+ <and>
+ <os family="windows" />
+ <canCreateSymlink/>
+ </and>
+ </condition>
<mkdir dir="${input}/foo" />
<touch file="${input}/foo/test"/>
<mkdir dir="${output}" />
@@ -49,13 +55,13 @@
<au:assertFileExists file="${output}/bar"/>
</target>
- <target name="XtestCreateFileSymlink" depends="setUp" if="isWindows">
+ <target name="testCreateFileSymlink" depends="setUp" if="canCreateSymlink">
<mklink linktype="file-symlink" link="${output}/bar"
targetFile="${input}/foo/test"/>
<au:assertFileExists file="${output}/bar"/>
</target>
- <target name="XtestCreateDirectorySymlink" depends="setUp" if="isWindows">
+ <target name="XtestCreateDirectorySymlink" depends="setUp"
if="canCreateSymlink">
<mklink linktype="dir-symlink" link="${output}/bar"
targetFile="${input}/foo"/>
<au:assertFileExists file="${output}/bar/test"/>