/* * Copyright 2014 Bernd Vogt and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.sourcepit.b2.internal.scm.svn; import static org.sourcepit.b2.files.ModuleDirectory.FLAG_DERIVED; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.inject.Named; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sourcepit.b2.execution.IB2Listener; import org.sourcepit.b2.files.FileVisitor; import org.sourcepit.b2.files.ModuleDirectory; import org.sourcepit.b2.model.module.AbstractModule; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNPropertyValue; import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager; import org.tmatesoft.svn.core.wc.SVNPropertyData; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc.SVNWCClient; import org.tmatesoft.svn.core.wc.SVNWCUtil; @Named public class SCM implements IB2Listener { private static final Logger LOGGER = LoggerFactory.getLogger(SCM.class); @Override public void startGeneration(ModuleDirectory moduleDirectory, AbstractModule module) { writeFileDump(moduleDirectory, module.getDirectory()); } public void doSetScmIgnores(ModuleDirectory moduleDirectory, AbstractModule module) { final List<File> fileDump = readFileDump(module.getDirectory()); final Collection<File> garbage = new LinkedHashSet<File>(); final Collection<File> newFiles = new LinkedHashSet<File>(); moduleDirectory.accept(new FileVisitor<RuntimeException>() { @Override public boolean visit(File file, int flags) { if ((FLAG_DERIVED & flags) != 0) { garbage.add(file); } else if (!fileDump.remove(file)) { newFiles.add(file); } return false; } }, true, true); garbage.addAll(newFiles); final Map<File, Collection<String>> dirToIgnoresMap = computePotentialIgnores(garbage); if (dirToIgnoresMap.isEmpty()) { return; } final SVNWCClient client = new SVNWCClient((ISVNAuthenticationManager) null, SVNWCUtil.createDefaultOptions(true)); for (Entry<File, Collection<String>> entry : dirToIgnoresMap.entrySet()) { final File dir = entry.getKey(); final Collection<String> potentialIgnores = entry.getValue(); final List<String> actualIgnores; try { actualIgnores = getSvnIgnores(client, dir); } catch (SVNException e) { LOGGER.warn("Faild to get actual ignore entries, error is: " + e.getMessage(), e); continue; } final Set<String> newIgnores = new LinkedHashSet<String>(); for (String potentialIgnore : potentialIgnores) { if (!actualIgnores.contains(potentialIgnore)) { newIgnores.add(potentialIgnore); } } if (!newIgnores.isEmpty()) { LOGGER.info("Ignore: " + dir.getPath()); LOGGER.info("Potential ignore entires: " + potentialIgnores); LOGGER.info("Actual ignore entires: " + actualIgnores); LOGGER.info("New ignore entires: " + newIgnores); final List<String> finalIgnores = new ArrayList<String>(actualIgnores); finalIgnores.addAll(newIgnores); try { setSvnIgnores(client, dir, finalIgnores); LOGGER.info("Set ignore entires: " + finalIgnores); } catch (SVNException e) { LOGGER.warn("Faild to set new ignore entries, error is: " + e.getMessage(), e); continue; } } } } private void writeFileDump(ModuleDirectory moduleDirectory, File moduleDir) { try { File dumFile = new File(moduleDir, ".b2/file.dump"); if (!dumFile.exists()) { dumFile.getParentFile().mkdirs(); dumFile.createNewFile(); } final BufferedWriter writer = new BufferedWriter(new FileWriter(dumFile)); try { moduleDirectory.accept(new FileVisitor<IOException>() { @Override public boolean visit(File file, int flags) throws IOException { writer.write(file.getAbsolutePath()); writer.newLine(); return true; } }, true, false); writer.flush(); } finally { IOUtils.closeQuietly(writer); } } catch (IOException e) { throw new IllegalStateException(e); } } private List<File> readFileDump(File moduleDir) { final List<File> fileDump = new ArrayList<File>(); File dumpFile = new File(moduleDir, ".b2/file.dump"); if (!dumpFile.exists()) { return fileDump; } BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(dumpFile)); String path = reader.readLine(); while (path != null) { fileDump.add(new File(path)); path = reader.readLine(); } } catch (IOException e) { throw new IllegalStateException(e); } finally { IOUtils.closeQuietly(reader); } return fileDump; } private Map<File, Collection<String>> computePotentialIgnores(final Collection<File> garbage) { final Map<File, Collection<String>> dirToIgnoresMap = new LinkedHashMap<File, Collection<String>>(); for (File file : garbage) { final File dir = file.getParentFile(); Collection<String> ignores = dirToIgnoresMap.get(dir); if (ignores == null) { ignores = new LinkedHashSet<String>(); dirToIgnoresMap.put(dir, ignores); } ignores.add(file.getName()); } return dirToIgnoresMap; } private static void setSvnIgnores(SVNWCClient client, File file, Collection<String> ignores) throws SVNException { final String propString; if (ignores == null || ignores.isEmpty()) { propString = null; } else { final StringBuilder sb = new StringBuilder(); for (String ignore : ignores) { sb.append(ignore); sb.append('\n'); sb.append('\n'); } sb.deleteCharAt(sb.length() - 1); propString = sb.toString(); } final SVNPropertyValue propValue = SVNPropertyValue.create(propString); client.doSetProperty(file, "svn:ignore", propValue, true, SVNDepth.EMPTY, null, null); } private static List<String> getSvnIgnores(SVNWCClient client, File file) throws SVNException { final List<String> ignores = new ArrayList<String>(); final SVNPropertyData svnProperty = client.doGetProperty(file, "svn:ignore", SVNRevision.WORKING, SVNRevision.WORKING); if (svnProperty == null) { return ignores; } final SVNPropertyValue value = svnProperty.getValue(); if (value == null) { return ignores; } final String rawIgnores = value.getString(); if (rawIgnores == null) { return ignores; } try { final BufferedReader rader = new BufferedReader(new StringReader(rawIgnores)); String line = rader.readLine(); while (line != null) { final String ignore = line.trim(); if (ignore.length() > 0) { ignores.add(ignore); } line = rader.readLine(); } } catch (IOException e) { throw new IllegalStateException(e); } return ignores; } }