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"/>

Reply via email to