Sophie

Sophie

distrib > Fedora > 18 > i386 > by-pkgid > fc7ae14813b1ce696ace37103365d8ee > files > 5

tycho-0.16.0-19.fc18.src.rpm

From e60a1cd8e0e9a9f4c04dafb092f1d5a142f25298 Mon Sep 17 00:00:00 2001
From: Jan Sievers <jan.sievers@sap.com>
Date: Fri, 9 Nov 2012 17:34:02 +0100
Subject: [PATCH] 361204 Evalute root permissions file patterns

- extract abstact superclass for FileSet
- introduce VirtualFileSet which can evaluate
  ant file patterns on a list of IPaths which
  simulate the resulting installation filesystem layout
- exntend existing integration test

Change-Id: I4f7ba9bcb2460e71c77ad706e9d70b2045df1592
---
 .../publisher/rootfiles/VirtualFileSetTest.java    |  50 ++++++++
 .../impl/publisher/rootfiles/AbstractFileSet.java  | 136 +++++++++++++++++++++
 .../publisher/rootfiles/FeatureRootAdvice.java     |   4 +-
 .../tycho/p2/impl/publisher/rootfiles/FileSet.java | 116 +-----------------
 .../p2/impl/publisher/rootfiles/FileToPathMap.java |   5 +
 .../publisher/rootfiles/RootFilesProperties.java   |  49 ++++++--
 .../publisher/rootfiles/RootPropertiesParser.java  |   7 ++
 .../impl/publisher/rootfiles/VirtualFileSet.java   |  44 +++++++
 .../example-feature/build.properties               |   1 +
 .../TYCHO465RootFiles/Tycho465RootFilesTest.java   |   7 ++
 10 files changed, 295 insertions(+), 124 deletions(-)
 create mode 100644 tycho-bundles/org.eclipse.tycho.p2.resolver.impl.test/src/test/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/VirtualFileSetTest.java
 create mode 100644 tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/AbstractFileSet.java
 create mode 100644 tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/VirtualFileSet.java
 create mode 100644 tycho-its/projects/TYCHO465RootFiles/example-feature/rootfiles2/dir/test.so

diff --git a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl.test/src/test/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/VirtualFileSetTest.java b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl.test/src/test/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/VirtualFileSetTest.java
new file mode 100644
index 0000000..fae52b9
--- /dev/null
+++ b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl.test/src/test/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/VirtualFileSetTest.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2012 SAP AG and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     SAP AG - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.tycho.p2.impl.publisher.rootfiles;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.junit.Test;
+
+public class VirtualFileSetTest {
+
+    @Test
+    public void testGetMatchingPaths() {
+        VirtualFileSet virtualFileSet = new VirtualFileSet("**/*.so*", createTestFileSystem(), true);
+        List<IPath> expected = createPathList("foo/bar/test.so", "lib1.so");
+        assertEquals(expected, virtualFileSet.getMatchingPaths());
+    }
+
+    private List<IPath> createPathList(String... paths) {
+        List<IPath> result = new ArrayList<IPath>();
+        for (String path : paths) {
+            result.add(Path.fromPortableString(path));
+        }
+        return result;
+    }
+
+    private Collection<IPath> createTestFileSystem() {
+        Collection<IPath> result = new ArrayList<IPath>();
+        String[] paths = new String[] { "foo/bar/test.so", "lib1.so", "testme.txt" };
+        for (String path : paths) {
+            result.add(Path.fromPortableString(path));
+        }
+        return result;
+    }
+
+}
diff --git a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/AbstractFileSet.java b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/AbstractFileSet.java
new file mode 100644
index 0000000..0304107
--- /dev/null
+++ b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/AbstractFileSet.java
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * Copyright (c) 2012 SAP AG and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     SAP AG - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.tycho.p2.impl.publisher.rootfiles;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.eclipse.core.runtime.IPath;
+
+public abstract class AbstractFileSet {
+
+    private static final String ZERO_OR_MORE_DIRS = "([^/]+/)*";
+    private static final String ZERO_OR_MORE_FILE_CHARACTERS = "[^/]*";
+    private static final String ONE_CHARACTER = ".";
+    private static final String QUOTE_BEGIN = "\\Q";
+    private static final String QUOTE_END = "\\E";
+
+    private static final String[] DEFAULTEXCLUDES = {
+            // Miscellaneous typical temporary files
+            "**/*~", "**/#*#", "**/.#*", "**/%*%", "**/._*",
+            // CVS
+            "**/CVS", "**/CVS/**", "**/.cvsignore",
+            // RCS
+            "**/RCS", "**/RCS/**",
+            // SCCS
+            "**/SCCS", "**/SCCS/**",
+            // Visual SourceSafe
+            "**/vssver.scc",
+            // Subversion
+            "**/.svn", "**/.svn/**",
+            // Arch
+            "**/.arch-ids", "**/.arch-ids/**",
+            //Bazaar
+            "**/.bzr", "**/.bzr/**",
+            //SurroundSCM
+            "**/.MySCMServerInfo",
+            // Mac
+            "**/.DS_Store",
+            // Serena Dimensions Version 10
+            "**/.metadata", "**/.metadata/**",
+            // Mercurial
+            "**/.hg", "**/.hg/**",
+            // git
+            "**/.git", "**/.git/**",
+            // BitKeeper
+            "**/BitKeeper", "**/BitKeeper/**", "**/ChangeSet", "**/ChangeSet/**",
+            // darcs
+            "**/_darcs", "**/_darcs/**", "**/.darcsrepo", "**/.darcsrepo/**", "**/-darcs-backup*",
+            "**/.darcs-temp-mail" };
+
+    protected Pattern includePattern;
+    protected List<Pattern> defaultExcludePatterns;
+    private boolean useDefaultExcludes;
+
+    public AbstractFileSet(String pattern, boolean useDefaultExcludes) {
+        this.useDefaultExcludes = useDefaultExcludes;
+        this.includePattern = convertToRegexPattern(pattern);
+        this.defaultExcludePatterns = createDefaultExcludePatterns();
+        this.defaultExcludePatterns = createDefaultExcludePatterns();
+    }
+
+    private List<Pattern> createDefaultExcludePatterns() {
+        List<Pattern> defaultExcludePatterns = new ArrayList<Pattern>();
+        for (String exclude : DEFAULTEXCLUDES) {
+            defaultExcludePatterns.add(convertToRegexPattern(exclude));
+        }
+        return defaultExcludePatterns;
+    }
+
+    private Pattern convertToRegexPattern(String antFilePattern) {
+        StringBuilder sb = new StringBuilder();
+        // always quote to make sure we don't interpret normal file 
+        // characters as special regexp characters
+        sb.append(QUOTE_BEGIN);
+        char[] chars = antFilePattern.toCharArray();
+        for (int i = 0; i < chars.length; i++) {
+            switch (chars[i]) {
+            case '?':
+                sb.append(QUOTE_END + ONE_CHARACTER + QUOTE_BEGIN);
+                break;
+            case '*':
+                sb.append(QUOTE_END);
+                if ((i + 1 < chars.length) && chars[i + 1] == '*') {
+                    // "**"
+                    sb.append(ZERO_OR_MORE_DIRS);
+                    i++;
+                    if ((i + 1 < chars.length) && chars[i + 1] == '/') {
+                        // "**/"
+                        // eat up slash since it is matched by ZERO_OR_MORE_DIRS
+                        i++;
+                    }
+                    if (i == chars.length - 1) {
+                        // "**" at end means we also match files
+                        sb.append(ZERO_OR_MORE_FILE_CHARACTERS);
+                    }
+                } else {
+                    // "*"
+                    sb.append(ZERO_OR_MORE_FILE_CHARACTERS);
+                }
+                sb.append(QUOTE_BEGIN);
+                break;
+            default:
+                sb.append(chars[i]);
+            }
+        }
+        sb.append(QUOTE_END);
+        return Pattern.compile(sb.toString());
+    }
+
+    /**
+     * @return <code>true</code> if the specified path matches the include pattern of this fileset
+     *         and not one of the default exclude patterns.
+     */
+    protected boolean matches(IPath path) {
+        String slashifiedPath = path.toPortableString();
+        if (useDefaultExcludes) {
+            for (Pattern excludePattern : defaultExcludePatterns) {
+                if (excludePattern.matcher(slashifiedPath).matches()) {
+                    return false;
+                }
+            }
+        }
+        return includePattern.matcher(slashifiedPath).matches();
+    }
+
+}
diff --git a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/FeatureRootAdvice.java b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/FeatureRootAdvice.java
index 3a11009..d1ec372 100644
--- a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/FeatureRootAdvice.java
+++ b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/FeatureRootAdvice.java
@@ -180,7 +180,9 @@ public class FeatureRootAdvice implements IFeatureRootAdvice {
 
     private void addPermissions(RootFilesProperties rootProperties, FileSetDescriptor rootFilesDescriptor) {
         for (RootFilesProperties.Permission permission : rootProperties.getPermissions()) {
-            rootFilesDescriptor.addPermissions(permission.toP2Format());
+            for (String[] p2Format : permission.toP2Formats()) {
+                rootFilesDescriptor.addPermissions(p2Format);
+            }
         }
     }
 
diff --git a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/FileSet.java b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/FileSet.java
index cc3190a..b715933 100644
--- a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/FileSet.java
+++ b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/FileSet.java
@@ -11,9 +11,6 @@
 package org.eclipse.tycho.p2.impl.publisher.rootfiles;
 
 import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.regex.Pattern;
 
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.Path;
@@ -23,53 +20,10 @@ import org.eclipse.core.runtime.Path;
  * {@link http://en.wikibooks.org/wiki/Apache_Ant/Fileset }. This is not a complete equivalent
  * implementation of the ant fileset. Only the subset needed for PDE root files is supported.
  */
-public class FileSet {
-
-    private static final String ZERO_OR_MORE_DIRS = "([^/]+/)*";
-    private static final String ZERO_OR_MORE_FILE_CHARACTERS = "[^/]*";
-    private static final String ONE_CHARACTER = ".";
-    private static final String QUOTE_BEGIN = "\\Q";
-    private static final String QUOTE_END = "\\E";
-
-    private static final String[] DEFAULTEXCLUDES = {
-            // Miscellaneous typical temporary files
-            "**/*~", "**/#*#", "**/.#*", "**/%*%", "**/._*",
-            // CVS
-            "**/CVS", "**/CVS/**", "**/.cvsignore",
-            // RCS
-            "**/RCS", "**/RCS/**",
-            // SCCS
-            "**/SCCS", "**/SCCS/**",
-            // Visual SourceSafe
-            "**/vssver.scc",
-            // Subversion
-            "**/.svn", "**/.svn/**",
-            // Arch
-            "**/.arch-ids", "**/.arch-ids/**",
-            //Bazaar
-            "**/.bzr", "**/.bzr/**",
-            //SurroundSCM
-            "**/.MySCMServerInfo",
-            // Mac
-            "**/.DS_Store",
-            // Serena Dimensions Version 10
-            "**/.metadata", "**/.metadata/**",
-            // Mercurial
-            "**/.hg", "**/.hg/**",
-            // git
-            "**/.git", "**/.git/**",
-            // BitKeeper
-            "**/BitKeeper", "**/BitKeeper/**", "**/ChangeSet", "**/ChangeSet/**",
-            // darcs
-            "**/_darcs", "**/_darcs/**", "**/.darcsrepo", "**/.darcsrepo/**", "**/-darcs-backup*",
-            "**/.darcs-temp-mail" };
+public class FileSet extends AbstractFileSet {
 
     private File baseDir;
 
-    private Pattern includePattern;
-    private List<Pattern> defaultExcludePatterns;
-    private boolean useDefaultExcludes;
-
     /**
      * Equivalent to {@link #FileSet(File, String, boolean)} with useDefaultExludes == true.
      */
@@ -89,34 +43,8 @@ public class FileSet {
      *            whether to use default file excludes for typical SCM metadata files.
      */
     public FileSet(File baseDir, String pattern, boolean useDefaultExcludes) {
+        super(pattern, useDefaultExcludes);
         this.baseDir = baseDir;
-        this.useDefaultExcludes = useDefaultExcludes;
-        this.includePattern = convertToRegexPattern(pattern);
-        this.defaultExcludePatterns = createDefaultExcludePatterns();
-    }
-
-    private List<Pattern> createDefaultExcludePatterns() {
-        List<Pattern> defaultExcludePatterns = new ArrayList<Pattern>();
-        for (String exclude : DEFAULTEXCLUDES) {
-            defaultExcludePatterns.add(convertToRegexPattern(exclude));
-        }
-        return defaultExcludePatterns;
-    }
-
-    /**
-     * @return <code>true</code> if the specified path matches the include pattern of this fileset
-     *         and not one of the default exclude patterns.
-     */
-    boolean matches(IPath path) {
-        String slashifiedPath = path.toPortableString();
-        if (useDefaultExcludes) {
-            for (Pattern excludePattern : defaultExcludePatterns) {
-                if (excludePattern.matcher(slashifiedPath).matches()) {
-                    return false;
-                }
-            }
-        }
-        return includePattern.matcher(slashifiedPath).matches();
     }
 
     public File getBaseDir() {
@@ -134,46 +62,6 @@ public class FileSet {
         return result;
     }
 
-    private Pattern convertToRegexPattern(String antFilePattern) {
-        StringBuilder sb = new StringBuilder();
-        // always quote to make sure we don't interpret normal file 
-        // characters as special regexp characters
-        sb.append(QUOTE_BEGIN);
-        char[] chars = antFilePattern.toCharArray();
-        for (int i = 0; i < chars.length; i++) {
-            switch (chars[i]) {
-            case '?':
-                sb.append(QUOTE_END + ONE_CHARACTER + QUOTE_BEGIN);
-                break;
-            case '*':
-                sb.append(QUOTE_END);
-                if ((i + 1 < chars.length) && chars[i + 1] == '*') {
-                    // "**"
-                    sb.append(ZERO_OR_MORE_DIRS);
-                    i++;
-                    if ((i + 1 < chars.length) && chars[i + 1] == '/') {
-                        // "**/"
-                        // eat up slash since it is matched by ZERO_OR_MORE_DIRS
-                        i++;
-                    }
-                    if (i == chars.length - 1) {
-                        // "**" at end means we also match files
-                        sb.append(ZERO_OR_MORE_FILE_CHARACTERS);
-                    }
-                } else {
-                    // "*"
-                    sb.append(ZERO_OR_MORE_FILE_CHARACTERS);
-                }
-                sb.append(QUOTE_BEGIN);
-                break;
-            default:
-                sb.append(chars[i]);
-            }
-        }
-        sb.append(QUOTE_END);
-        return Pattern.compile(sb.toString());
-    }
-
     private void recursiveScan(File file, FileToPathMap result, IPath baseDirPath) {
         if (file.isDirectory()) {
             for (File subFile : file.listFiles()) {
diff --git a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/FileToPathMap.java b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/FileToPathMap.java
index b18d116..a7033f2 100644
--- a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/FileToPathMap.java
+++ b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/FileToPathMap.java
@@ -12,6 +12,7 @@ package org.eclipse.tycho.p2.impl.publisher.rootfiles;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map.Entry;
 import java.util.Set;
@@ -34,6 +35,10 @@ public class FileToPathMap {
         map.put(canonify(key), value);
     }
 
+    public Collection<IPath> values() {
+        return map.values();
+    }
+
     public IPath get(File key) {
         return map.get(canonify(key));
     }
diff --git a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/RootFilesProperties.java b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/RootFilesProperties.java
index f3f8837..e0344c5 100644
--- a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/RootFilesProperties.java
+++ b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/RootFilesProperties.java
@@ -12,28 +12,52 @@ package org.eclipse.tycho.p2.impl.publisher.rootfiles;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IPath;
 
 public class RootFilesProperties {
 
     public class Permission {
-        private final String path;
+        private final String pathPattern;
+        private Set<String> resolvedPaths;
+
+        // 3-digit octal file permission mask 
+        private final String chmodPermissions;
+        private boolean isResolved = false;
 
-        private final String chmodPermissionPattern;
+        public Permission(String pathPattern, String chmodPermissions) {
+            this.pathPattern = pathPattern;
+            this.chmodPermissions = chmodPermissions;
+        }
 
-        public Permission(String path, String chmodPermissionPattern) {
-            this.path = path;
-            this.chmodPermissionPattern = chmodPermissionPattern;
+        void resolveWildcards(Collection<IPath> virtualFiles, boolean useDefaultExcludes) {
+            resolvedPaths = new HashSet<String>();
+            VirtualFileSet virtualFileSet = new VirtualFileSet(pathPattern, virtualFiles, useDefaultExcludes);
+            for (IPath path : virtualFileSet.getMatchingPaths()) {
+                resolvedPaths.add(path.toString());
+            }
+            isResolved = true;
         }
 
-        public String[] toP2Format() {
-            return new String[] { chmodPermissionPattern, path };
+        public List<String[]> toP2Formats() {
+            if (!isResolved) {
+                throw new IllegalStateException("must call resolveWildcards() first");
+            }
+            List<String[]> p2Formats = new ArrayList<String[]>(resolvedPaths.size());
+            for (String resolvedPath : resolvedPaths) {
+                String[] p2Format = new String[] { chmodPermissions, resolvedPath };
+                p2Formats.add(p2Format);
+            }
+            return p2Formats;
         }
     }
 
     /**
-     * Absolute source location of a root file to the relative path that describes the location of
-     * the root file in the installed product.
+     * Absolute source location of a root file to the relative pathPattern that describes the
+     * location of the root file in the installed product.
      */
     private FileToPathMap fileSourceToDestinationMap = new FileToPathMap();
 
@@ -70,6 +94,13 @@ public class RootFilesProperties {
         }
     }
 
+    public void resolvePermissionWildcards(boolean useDefaultExcludes) {
+        Collection<IPath> allFilePaths = fileSourceToDestinationMap.values();
+        for (Permission permission : permissions) {
+            permission.resolveWildcards(allFilePaths, useDefaultExcludes);
+        }
+    }
+
     private static void verifySpecifiedInPairs(String[] linkValueSegments) {
         if (linkValueSegments.length % 2 != 0) {
             String message = "Links must be specified as a sequence of \"link target,link name\" pairs; the actual value \""
diff --git a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/RootPropertiesParser.java b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/RootPropertiesParser.java
index 822c995..64c994d 100644
--- a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/RootPropertiesParser.java
+++ b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/RootPropertiesParser.java
@@ -94,6 +94,13 @@ public class RootPropertiesParser {
             valueSegments = splitAndTrimValue(entry.getValue());
             parseBuildPropertiesLine();
         }
+        resolvePermissionWildcards();
+    }
+
+    private void resolvePermissionWildcards() {
+        for (RootFilesProperties rootProperty : parsingResult.getPropertiesPerConfigMap().values()) {
+            rootProperty.resolvePermissionWildcards(useDefaultExcludes);
+        }
     }
 
     private static String[] splitKey(String string) {
diff --git a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/VirtualFileSet.java b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/VirtualFileSet.java
new file mode 100644
index 0000000..55088a0
--- /dev/null
+++ b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/rootfiles/VirtualFileSet.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2012 SAP AG and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     SAP AG - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.tycho.p2.impl.publisher.rootfiles;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * Allows to evaluate ant file patterns on a simulated filesystem layout of the resulting
+ * installation (as opposed to the filesystem layout during build). Needed for computing chmod
+ * permissions applied to files with wildcards.
+ */
+public class VirtualFileSet extends AbstractFileSet {
+
+    private Collection<IPath> paths;
+
+    public VirtualFileSet(String antFilePattern, Collection<IPath> virtualFileSystem, boolean useDefaultExcludes) {
+        super(antFilePattern, useDefaultExcludes);
+        this.paths = virtualFileSystem;
+    }
+
+    public List<IPath> getMatchingPaths() {
+        List<IPath> matchingPaths = new ArrayList<IPath>();
+        for (IPath path : paths) {
+            if (matches(path)) {
+                matchingPaths.add(path);
+            }
+        }
+        return matchingPaths;
+    }
+
+}
diff --git a/tycho-its/projects/TYCHO465RootFiles/example-feature/build.properties b/tycho-its/projects/TYCHO465RootFiles/example-feature/build.properties
index 04b8956..4ceff04 100644
--- a/tycho-its/projects/TYCHO465RootFiles/example-feature/build.properties
+++ b/tycho-its/projects/TYCHO465RootFiles/example-feature/build.properties
@@ -15,3 +15,4 @@ root.win32.win32.x86 = rootfiles
 root.permissions.755 = file5.txt
 root.link = dir/file6.txt,alias_file6.txt
 root.linux.gtk.x86_64.link = file1.txt,alias_file1.txt
+root.linux.gtk.x86_64.permissions.555 = **/*.so
diff --git a/tycho-its/projects/TYCHO465RootFiles/example-feature/rootfiles2/dir/test.so b/tycho-its/projects/TYCHO465RootFiles/example-feature/rootfiles2/dir/test.so
new file mode 100644
index 0000000..e69de29
diff --git a/tycho-its/src/test/java/org/eclipse/tycho/test/TYCHO465RootFiles/Tycho465RootFilesTest.java b/tycho-its/src/test/java/org/eclipse/tycho/test/TYCHO465RootFiles/Tycho465RootFilesTest.java
index a11f50d..92ef1fd 100644
--- a/tycho-its/src/test/java/org/eclipse/tycho/test/TYCHO465RootFiles/Tycho465RootFilesTest.java
+++ b/tycho-its/src/test/java/org/eclipse/tycho/test/TYCHO465RootFiles/Tycho465RootFilesTest.java
@@ -239,6 +239,13 @@ public class Tycho465RootFilesTest extends AbstractTychoIntegrationTest {
         assertTrue(
                 "Expected chmod touchpointData instruction '" + expectedTouchpointDataInstruction + "' not found.",
                 Util.iuHasTouchpointDataInstruction(featureRootIus.iterator().next(), expectedTouchpointDataInstruction));
+        // permission defined in build.properties: root.linux.gtk.x86_64.permissions.555 = **/*.so
+        Element linuxRootIu = Util.findIU(contentXml, "tycho465.feature_root.gtk.linux.x86_64").iterator().next();
+
+        String chmod555Instruction = "chmod(targetDir:${installFolder}, targetFile:dir/test.so, permissions:555);";
+
+        assertTrue("Expected chmod touchpointData instruction '" + chmod555Instruction + "' not found.",
+                Util.iuHasTouchpointDataInstruction(linuxRootIu, chmod555Instruction));
     }
 
     static void assertRootIuLinksMetaData(Document contentXml) {
-- 
1.7.11.7