Odd commit related to licenser. Maybe line ending problems?

This commit is contained in:
Brian Caruso 2013-08-27 15:27:16 -04:00
parent 50945d5af1
commit 22d7e04573
5 changed files with 896 additions and 896 deletions

View file

@ -1,123 +1,123 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */ /* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.utilities.revisioninfo; package edu.cornell.mannlib.vitro.utilities.revisioninfo;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
/** /**
* Parse the response that we got from SVN info. * Parse the response that we got from SVN info.
* *
* Not thread-safe. * Not thread-safe.
*/ */
public class InfoResponseParser { public class InfoResponseParser {
private static final Pattern URL_PATTERN = Pattern.compile("URL: (\\S+)"); private static final Pattern URL_PATTERN = Pattern.compile("URL: (\\S+)");
private static final Pattern ROOT_PATTERN = Pattern private static final Pattern ROOT_PATTERN = Pattern
.compile("Repository Root: (\\S+)"); .compile("Repository Root: (\\S+)");
private static final String TRUNK_PREFIX = "/trunk"; private static final String TRUNK_PREFIX = "/trunk";
private static final String TAGS_PREFIX = "/tags/"; private static final String TAGS_PREFIX = "/tags/";
private static final String BRANCHES_PREFIX = "/branches/"; private static final String BRANCHES_PREFIX = "/branches/";
private final String infoResponse; private final String infoResponse;
private String path; private String path;
public InfoResponseParser(String infoResponse) { public InfoResponseParser(String infoResponse) {
this.infoResponse = infoResponse; this.infoResponse = infoResponse;
} }
public String parse() { public String parse() {
try { try {
path = figurePath(); path = figurePath();
if (isTrunkPath()) { if (isTrunkPath()) {
return "trunk"; return "trunk";
} else if (isTagPath()) { } else if (isTagPath()) {
return "tag " + getTagName(); return "tag " + getTagName();
} else if (isBranchPath()) { } else if (isBranchPath()) {
return "branch " + getBranchName(); return "branch " + getBranchName();
} else { } else {
return null; return null;
} }
} catch (Exception e) { } catch (Exception e) {
System.err.println(e); System.err.println(e);
return null; return null;
} }
} }
private String figurePath() throws Exception { private String figurePath() throws Exception {
if (infoResponse == null) { if (infoResponse == null) {
throw new Exception("infoResponse is null."); throw new Exception("infoResponse is null.");
} }
String url = getUrlFromResponse(); String url = getUrlFromResponse();
String root = getRootFromResponse(); String root = getRootFromResponse();
if (!url.startsWith(root)) { if (!url.startsWith(root)) {
throw new Exception("url doesn't start with root."); throw new Exception("url doesn't start with root.");
} }
return url.substring(root.length()); return url.substring(root.length());
} }
private String getUrlFromResponse() throws Exception { private String getUrlFromResponse() throws Exception {
return findNonEmptyMatch(URL_PATTERN, 1); return findNonEmptyMatch(URL_PATTERN, 1);
} }
private String getRootFromResponse() throws Exception { private String getRootFromResponse() throws Exception {
return findNonEmptyMatch(ROOT_PATTERN, 1); return findNonEmptyMatch(ROOT_PATTERN, 1);
} }
private String findNonEmptyMatch(Pattern pattern, int groupIndex) private String findNonEmptyMatch(Pattern pattern, int groupIndex)
throws Exception { throws Exception {
Matcher matcher = pattern.matcher(infoResponse); Matcher matcher = pattern.matcher(infoResponse);
if (!matcher.find()) { if (!matcher.find()) {
throw new Exception("no match with '" + pattern + "'. Is your Subversion client out of date?"); throw new Exception("no match with '" + pattern + "'. Is your Subversion client out of date?");
} }
String value = matcher.group(groupIndex); String value = matcher.group(groupIndex);
if ((value == null) || (value.isEmpty())) { if ((value == null) || (value.isEmpty())) {
throw new Exception("match with '" + pattern + "' is empty."); throw new Exception("match with '" + pattern + "' is empty.");
} }
return value; return value;
} }
private boolean isTrunkPath() { private boolean isTrunkPath() {
return path.startsWith(TRUNK_PREFIX); return path.startsWith(TRUNK_PREFIX);
} }
private boolean isTagPath() { private boolean isTagPath() {
return path.startsWith(TAGS_PREFIX); return path.startsWith(TAGS_PREFIX);
} }
private String getTagName() { private String getTagName() {
return getFirstLevel(discardPrefix(path, TAGS_PREFIX)); return getFirstLevel(discardPrefix(path, TAGS_PREFIX));
} }
private boolean isBranchPath() { private boolean isBranchPath() {
return path.startsWith(BRANCHES_PREFIX); return path.startsWith(BRANCHES_PREFIX);
} }
private String getBranchName() { private String getBranchName() {
return getFirstLevel(discardPrefix(path, BRANCHES_PREFIX)); return getFirstLevel(discardPrefix(path, BRANCHES_PREFIX));
} }
private String discardPrefix(String string, String prefix) { private String discardPrefix(String string, String prefix) {
if (string.length() < prefix.length()) { if (string.length() < prefix.length()) {
return ""; return "";
} else { } else {
return string.substring(prefix.length()); return string.substring(prefix.length());
} }
} }
private String getFirstLevel(String string) { private String getFirstLevel(String string) {
int slashHere = string.indexOf('/'); int slashHere = string.indexOf('/');
if (slashHere == -1) { if (slashHere == -1) {
return string; return string;
} else { } else {
return string.substring(0, slashHere); return string.substring(0, slashHere);
} }
} }
} }

View file

@ -1,230 +1,230 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */ /* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.utilities.revisioninfo; package edu.cornell.mannlib.vitro.utilities.revisioninfo;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.EOFException; import java.io.EOFException;
import java.io.File; import java.io.File;
import java.io.FileReader; import java.io.FileReader;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.io.Writer; import java.io.Writer;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import edu.cornell.mannlib.vitro.utilities.revisioninfo.ProcessRunner.ProcessException; import edu.cornell.mannlib.vitro.utilities.revisioninfo.ProcessRunner.ProcessException;
/** /**
* Get release and revision information to display on screen. Put this * Get release and revision information to display on screen. Put this
* information into a single line and append it to the specified file. * information into a single line and append it to the specified file.
* *
* Ask Subversion for the information. If Subversion is available, and if this * Ask Subversion for the information. If Subversion is available, and if this
* is a working directory, then we can build the info from the responses we get * is a working directory, then we can build the info from the responses we get
* from "svn info" and "svnversion". * from "svn info" and "svnversion".
* *
* If that doesn't work, read the information from the "revisionInfo" file in * If that doesn't work, read the information from the "revisionInfo" file in
* the product directory. Presumably, that file was created when the source was * the product directory. Presumably, that file was created when the source was
* exported from Subversion. * exported from Subversion.
* *
* If that doesn't work either, return something like this: * If that doesn't work either, return something like this:
* "productName ~ unknown ~ unknown" * "productName ~ unknown ~ unknown"
*/ */
public class RevisionInfoBuilder { public class RevisionInfoBuilder {
/** /**
* Indicates a problem with the command-line arguments. * Indicates a problem with the command-line arguments.
*/ */
private static class UsageException extends Exception { private static class UsageException extends Exception {
UsageException(String message) { UsageException(String message) {
super(message); super(message);
} }
} }
/** /**
* An object that holds the revision information and a message about how we * An object that holds the revision information and a message about how we
* obtained it. * obtained it.
*/ */
private static class Results { private static class Results {
final String message; final String message;
final String infoLine; final String infoLine;
Results(String message, String infoLine) { Results(String message, String infoLine) {
this.message = message; this.message = message;
this.infoLine = infoLine; this.infoLine = infoLine;
} }
public String toString() { public String toString() {
return message + ": " + infoLine; return message + ": " + infoLine;
} }
} }
private static final String SVN_DIRECTORY_NAME = ".svn"; private static final String SVN_DIRECTORY_NAME = ".svn";
private static final String[] SVNVERSION_COMMAND = { "svnversion", "." }; private static final String[] SVNVERSION_COMMAND = { "svnversion", "." };
private static final String[] SVN_INFO_COMMAND = { "svn", "info" }; private static final String[] SVN_INFO_COMMAND = { "svn", "info" };
private static final String INFO_LINE_DELIMITER = " ~ "; private static final String INFO_LINE_DELIMITER = " ~ ";
private static final String REVISION_INFO_FILENAME = "revisionInfo"; private static final String REVISION_INFO_FILENAME = "revisionInfo";
private final String productName; private final String productName;
private final File productDirectory; private final File productDirectory;
private final File resultFile; private final File resultFile;
private Results results; private Results results;
public RevisionInfoBuilder(String[] args) throws UsageException { public RevisionInfoBuilder(String[] args) throws UsageException {
if (args.length != 3) { if (args.length != 3) {
throw new UsageException( throw new UsageException(
"RevisionInfoBuilder requires 3 arguments, not " "RevisionInfoBuilder requires 3 arguments, not "
+ args.length); + args.length);
} }
productName = args[0]; productName = args[0];
productDirectory = new File(args[1]); productDirectory = new File(args[1]);
resultFile = new File(args[2]); resultFile = new File(args[2]);
if (!productDirectory.isDirectory()) { if (!productDirectory.isDirectory()) {
throw new UsageException("Directory '" throw new UsageException("Directory '"
+ productDirectory.getAbsolutePath() + "' does not exist."); + productDirectory.getAbsolutePath() + "' does not exist.");
} }
if (!resultFile.getParentFile().exists()) { if (!resultFile.getParentFile().exists()) {
throw new UsageException("Result file '" throw new UsageException("Result file '"
+ resultFile.getAbsolutePath() + resultFile.getAbsolutePath()
+ "' does not exist, and we can't create it " + "' does not exist, and we can't create it "
+ "because it's parent directory doesn't exist either."); + "because it's parent directory doesn't exist either.");
} }
} }
private void buildInfo() { private void buildInfo() {
results = buildInfoFromSubversion(); results = buildInfoFromSubversion();
if (results == null) { if (results == null) {
results = buildInfoFromFile(); results = buildInfoFromFile();
} }
if (results == null) { if (results == null) {
results = buildDummyInfo(); results = buildDummyInfo();
} }
} }
private Results buildInfoFromSubversion() { private Results buildInfoFromSubversion() {
if (!isThisASubversionWorkspace()) { if (!isThisASubversionWorkspace()) {
System.out.println("Not a Subversion workspace"); System.out.println("Not a Subversion workspace");
return null; return null;
} }
String release = assembleReleaseNameFromSubversion(); String release = assembleReleaseNameFromSubversion();
if (release == null) { if (release == null) {
System.out.println("Couldn't get release name from Subversion"); System.out.println("Couldn't get release name from Subversion");
return null; return null;
} }
String revision = obtainRevisionLevelFromSubversion(); String revision = obtainRevisionLevelFromSubversion();
if (revision == null) { if (revision == null) {
System.out.println("Couldn't get revision level from Subversion"); System.out.println("Couldn't get revision level from Subversion");
return null; return null;
} }
return new Results("Info from Subversion", buildLine(release, revision)); return new Results("Info from Subversion", buildLine(release, revision));
} }
private boolean isThisASubversionWorkspace() { private boolean isThisASubversionWorkspace() {
File svnDirectory = new File(productDirectory, SVN_DIRECTORY_NAME); File svnDirectory = new File(productDirectory, SVN_DIRECTORY_NAME);
return svnDirectory.isDirectory(); return svnDirectory.isDirectory();
} }
private String assembleReleaseNameFromSubversion() { private String assembleReleaseNameFromSubversion() {
String infoResponse = runSubProcess(SVN_INFO_COMMAND); String infoResponse = runSubProcess(SVN_INFO_COMMAND);
return new InfoResponseParser(infoResponse).parse(); return new InfoResponseParser(infoResponse).parse();
} }
private String obtainRevisionLevelFromSubversion() { private String obtainRevisionLevelFromSubversion() {
String response = runSubProcess(SVNVERSION_COMMAND); String response = runSubProcess(SVNVERSION_COMMAND);
return (response == null) ? null : response.trim(); return (response == null) ? null : response.trim();
} }
private String runSubProcess(String[] cmdArray) { private String runSubProcess(String[] cmdArray) {
List<String> command = Arrays.asList(cmdArray); List<String> command = Arrays.asList(cmdArray);
try { try {
ProcessRunner runner = new ProcessRunner(); ProcessRunner runner = new ProcessRunner();
runner.setWorkingDirectory(productDirectory); runner.setWorkingDirectory(productDirectory);
runner.run(command); runner.run(command);
int rc = runner.getReturnCode(); int rc = runner.getReturnCode();
if (rc != 0) { if (rc != 0) {
throw new ProcessRunner.ProcessException("Return code from " throw new ProcessRunner.ProcessException("Return code from "
+ command + " was " + rc); + command + " was " + rc);
} }
String output = runner.getStdOut(); String output = runner.getStdOut();
// System.err.println(command + " response was '" + output + "'"); // System.err.println(command + " response was '" + output + "'");
return output; return output;
} catch (ProcessException e) { } catch (ProcessException e) {
return null; return null;
} }
} }
private Results buildInfoFromFile() { private Results buildInfoFromFile() {
try { try {
File revisionInfoFile = new File(productDirectory, File revisionInfoFile = new File(productDirectory,
REVISION_INFO_FILENAME); REVISION_INFO_FILENAME);
BufferedReader reader = new BufferedReader(new FileReader( BufferedReader reader = new BufferedReader(new FileReader(
revisionInfoFile)); revisionInfoFile));
String release = reader.readLine(); String release = reader.readLine();
if (release == null) { if (release == null) {
throw new EOFException("No release line in file."); throw new EOFException("No release line in file.");
} }
String revision = reader.readLine(); String revision = reader.readLine();
if (revision == null) { if (revision == null) {
throw new EOFException("No revision line in file."); throw new EOFException("No revision line in file.");
} }
return new Results("Info from file", buildLine(release, revision)); return new Results("Info from file", buildLine(release, revision));
} catch (IOException e) { } catch (IOException e) {
System.out.println("No information from file: " + e); System.out.println("No information from file: " + e);
return null; return null;
} }
} }
private Results buildDummyInfo() { private Results buildDummyInfo() {
String line = buildLine(null, null); String line = buildLine(null, null);
return new Results("Using dummy info", line); return new Results("Using dummy info", line);
} }
private String buildLine(String release, String revision) { private String buildLine(String release, String revision) {
if (release == null) { if (release == null) {
release = "unknown"; release = "unknown";
} }
if (revision == null) { if (revision == null) {
revision = "unknown"; revision = "unknown";
} }
return productName + INFO_LINE_DELIMITER + release.trim() return productName + INFO_LINE_DELIMITER + release.trim()
+ INFO_LINE_DELIMITER + revision.trim(); + INFO_LINE_DELIMITER + revision.trim();
} }
private void writeLine() throws IOException { private void writeLine() throws IOException {
Writer writer = null; Writer writer = null;
writer = new FileWriter(resultFile, true); writer = new FileWriter(resultFile, true);
writer.write(results.infoLine + "\n"); writer.write(results.infoLine + "\n");
writer.close(); writer.close();
System.out.println(results); System.out.println(results);
} }
public static void main(String[] args) { public static void main(String[] args) {
try { try {
RevisionInfoBuilder builder = new RevisionInfoBuilder(args); RevisionInfoBuilder builder = new RevisionInfoBuilder(args);
builder.buildInfo(); builder.buildInfo();
builder.writeLine(); builder.writeLine();
} catch (UsageException e) { } catch (UsageException e) {
System.err.println(e); System.err.println(e);
System.err.println("usage: RevisionInfoBuilder [product_name] " System.err.println("usage: RevisionInfoBuilder [product_name] "
+ "[product_directory] [output_file]"); + "[product_directory] [output_file]");
System.exit(1); System.exit(1);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
System.exit(1); System.exit(1);
} }
} }
} }

View file

@ -1,411 +1,411 @@
=begin =begin
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
Scan the source directory, checking for expected "magic license tags", Scan the source directory, checking for expected "magic license tags",
or or
Copy the source directory, inserting licensing information into the files. Copy the source directory, inserting licensing information into the files.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
=end =end
$: << File.dirname(File.expand_path(__FILE__)) $: << File.dirname(File.expand_path(__FILE__))
require 'date' require 'date'
require 'fileutils' require 'fileutils'
require 'pathname' require 'pathname'
require 'property_file_reader' require 'property_file_reader'
require 'licenser_stats' require 'licenser_stats'
class Licenser class Licenser
MAGIC_STRING = '$This file is distributed under the terms of the license in /doc/license.txt$' MAGIC_STRING = '$This file is distributed under the terms of the license in /doc/license.txt$'
# ------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------
private private
# ------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------
# #
# Some paths in the properties file, if they are relative, should be relative to the # Some paths in the properties file, if they are relative, should be relative to the
# properties file itself. # properties file itself.
def relative_to_properties(properties, key) def relative_to_properties(properties, key)
path = properties[key] path = properties[key]
base = File.dirname(properties['properties_file_path']) base = File.dirname(properties['properties_file_path'])
return nil if path == nil return nil if path == nil
return File.expand_path(path) if Pathname.new(path).absolute? return File.expand_path(path) if Pathname.new(path).absolute?
return File.expand_path(File.join(base, path)) return File.expand_path(File.join(base, path))
end end
# Some paths in the properties file, if they are relative, should be relative to the # Some paths in the properties file, if they are relative, should be relative to the
# source directory. # source directory.
def relative_to_source(properties, key) def relative_to_source(properties, key)
path = properties[key] path = properties[key]
base = @source_dir ? @source_dir : '' base = @source_dir ? @source_dir : ''
return nil if path == nil return nil if path == nil
return path if Pathname.new(path).absolute? return path if Pathname.new(path).absolute?
return File.expand_path(File.join(base, path)) return File.expand_path(File.join(base, path))
end end
# Confirm that the parameters are reasonable. # Confirm that the parameters are reasonable.
# #
def sanity_checks_on_parameters() def sanity_checks_on_parameters()
# Check that all necessary properties are here. # Check that all necessary properties are here.
raise("Properties file must contain a value for 'scan_only'") if @scan_only_string == nil raise("Properties file must contain a value for 'scan_only'") if @scan_only_string == nil
raise("Properties file must contain a value for 'source_dir'") if @source_dir == nil raise("Properties file must contain a value for 'source_dir'") if @source_dir == nil
raise("Properties file must contain a value for 'known_exceptions'") if @known_exceptions_file == nil raise("Properties file must contain a value for 'known_exceptions'") if @known_exceptions_file == nil
raise("Properties file must contain a value for 'skip_directories'") if @skip_directories_list == nil raise("Properties file must contain a value for 'skip_directories'") if @skip_directories_list == nil
raise("Properties file must contain a value for 'file_matchers'") if @file_match_list == nil raise("Properties file must contain a value for 'file_matchers'") if @file_match_list == nil
raise("Properties file must contain a value for 'report_level'") if @report_level_string == nil raise("Properties file must contain a value for 'report_level'") if @report_level_string == nil
if !File.exist?(@source_dir) if !File.exist?(@source_dir)
raise "Source directory does not exist: #{@source_dir}" raise "Source directory does not exist: #{@source_dir}"
end end
if !File.exist?(@known_exceptions_file) if !File.exist?(@known_exceptions_file)
raise "Known exceptions file does not exist: #{@known_exceptions_file}" raise "Known exceptions file does not exist: #{@known_exceptions_file}"
end end
if !@scan_only if !@scan_only
raise("Properties file must contain a value for 'target_dir'") if @target_dir == nil raise("Properties file must contain a value for 'target_dir'") if @target_dir == nil
raise("Properties file must contain a value for 'license_file'") if @license_file == nil raise("Properties file must contain a value for 'license_file'") if @license_file == nil
if File.exist?(@target_dir) if File.exist?(@target_dir)
raise "Target directory already exists: #{@target_dir}" raise "Target directory already exists: #{@target_dir}"
end end
target_parent = File.dirname(@target_dir) target_parent = File.dirname(@target_dir)
if !File.exist?(target_parent) if !File.exist?(target_parent)
raise "Path to target directory doesn't exist: #{target_parent}" raise "Path to target directory doesn't exist: #{target_parent}"
end end
if !File.exist?(@license_file) if !File.exist?(@license_file)
raise "License file does not exist: #{@license_file}" raise "License file does not exist: #{@license_file}"
end end
end end
end end
# Prepare the license as an array of lines of text, # Prepare the license as an array of lines of text,
# with the current year substituted in for ${year} # with the current year substituted in for ${year}
# #
def prepare_license_text(license_file) def prepare_license_text(license_file)
if (license_file == nil) if (license_file == nil)
return [] return []
end end
year_string = DateTime.now.year.to_s year_string = DateTime.now.year.to_s
text = [] text = []
File.open(license_file) do |file| File.open(license_file) do |file|
file.each do |line| file.each do |line|
text << line.gsub('${year}', year_string) text << line.gsub('${year}', year_string)
end end
end end
return text return text
end end
# The globs in the exceptions file are assumed to be # The globs in the exceptions file are assumed to be
# relative to the source directory. Make them explicitly so. # relative to the source directory. Make them explicitly so.
# #
# Ignore any blank lines or lines that start with a '#' # Ignore any blank lines or lines that start with a '#'
# #
def prepare_exception_globs(exceptions_file, source_dir) def prepare_exception_globs(exceptions_file, source_dir)
source_path = File.expand_path(source_dir) source_path = File.expand_path(source_dir)
globs = [] globs = []
File.open(exceptions_file) do |file| File.open(exceptions_file) do |file|
file.each do |line| file.each do |line|
glob = line.strip glob = line.strip
if (glob.length > 0) && (glob[0..0] != '#') if (glob.length > 0) && (glob[0..0] != '#')
globs << "#{source_path}/#{glob}".gsub('//', '/') globs << "#{source_path}/#{glob}".gsub('//', '/')
end end
end end
end end
return globs return globs
end end
# Recursively scan this directory, and copy if we are not scan-only. # Recursively scan this directory, and copy if we are not scan-only.
# #
def scan_dir(source_dir, target_dir) def scan_dir(source_dir, target_dir)
@stats.enter_directory(source_dir) @stats.enter_directory(source_dir)
Dir.mkdir(File.expand_path(target_dir, @target_dir)) if !@scan_only Dir.mkdir(File.expand_path(target_dir, @target_dir)) if !@scan_only
Dir.foreach(File.join(@source_dir, source_dir)) do |filename| Dir.foreach(File.join(@source_dir, source_dir)) do |filename|
source_path_relative = File.join(source_dir, filename) source_path_relative = File.join(source_dir, filename)
source_path = File.join(@source_dir, source_path_relative) source_path = File.join(@source_dir, source_path_relative)
target_path_relative = File.join(target_dir, filename) target_path_relative = File.join(target_dir, filename)
target_path = File.join(@target_dir, target_path_relative) target_path = File.join(@target_dir, target_path_relative)
# What kind of beast is this? # What kind of beast is this?
if filename == '.' || filename == '..' if filename == '.' || filename == '..'
is_skipped_directory = true is_skipped_directory = true
else else
if File.directory?(source_path) if File.directory?(source_path)
if (path_matches_skipped?(source_path_relative)) if (path_matches_skipped?(source_path_relative))
is_skipped_directory = true is_skipped_directory = true
else else
is_directory = true is_directory = true
end end
else else
if filename_matches_pattern?(filename) if filename_matches_pattern?(filename)
if path_matches_exception?(source_path_relative) if path_matches_exception?(source_path_relative)
is_exception = true is_exception = true
else else
is_match = true is_match = true
end end
else else
is_ignored = true is_ignored = true
end end
end end
end end
if is_skipped_directory if is_skipped_directory
# do nothing # do nothing
elsif is_directory elsif is_directory
scan_dir(source_path_relative, target_path_relative) scan_dir(source_path_relative, target_path_relative)
elsif is_match elsif is_match
if @scan_only if @scan_only
@stats.record_scan_matching(filename) @stats.record_scan_matching(filename)
scan_file(source_path, filename) scan_file(source_path, filename)
else else
@stats.record_copy_matching(filename) @stats.record_copy_matching(filename)
copy_file_with_license(source_path, target_path, filename) copy_file_with_license(source_path, target_path, filename)
end end
elsif is_exception elsif is_exception
@stats.record_known_exception(filename) @stats.record_known_exception(filename)
if @scan_only if @scan_only
# do nothing # do nothing
else else
copy_file_without_license(source_path, target_path) copy_file_without_license(source_path, target_path)
end end
else # not a match else # not a match
if @scan_only if @scan_only
@stats.record_scan_non_matching(filename) @stats.record_scan_non_matching(filename)
else else
@stats.record_copy_non_matching(filename) @stats.record_copy_non_matching(filename)
copy_file_without_license(source_path, target_path) copy_file_without_license(source_path, target_path)
end end
end end
end end
end end
# Is this directory one of the skipped? # Is this directory one of the skipped?
# #
def path_matches_skipped?(relative_path) def path_matches_skipped?(relative_path)
@skip_directories.each do |glob| @skip_directories.each do |glob|
return true if File.fnmatch(glob, relative_path) return true if File.fnmatch(glob, relative_path)
end end
return false return false
end end
# Does this file path match any of the exceptions? # Does this file path match any of the exceptions?
# #
def path_matches_exception?(relative_path) def path_matches_exception?(relative_path)
path = File.expand_path(File.join(@source_dir, relative_path)) path = File.expand_path(File.join(@source_dir, relative_path))
@known_exceptions.each do |pattern| @known_exceptions.each do |pattern|
return true if File.fnmatch(pattern, path) return true if File.fnmatch(pattern, path)
end end
return false return false
end end
# Does this filename match any of the patterns? # Does this filename match any of the patterns?
# #
def filename_matches_pattern?(filename) def filename_matches_pattern?(filename)
@file_matchers.each do |pattern| @file_matchers.each do |pattern|
return true if File.fnmatch(pattern, filename) return true if File.fnmatch(pattern, filename)
end end
return false return false
end end
# This file would be eligible for licensing if we weren't in scan-only mode. # This file would be eligible for licensing if we weren't in scan-only mode.
# #
def scan_file(source_path, filename) def scan_file(source_path, filename)
found = 0 found = 0
File.open(source_path) do |source_file| File.open(source_path) do |source_file|
source_file.each do |line| source_file.each do |line|
if line.include?(MAGIC_STRING) if line.include?(MAGIC_STRING)
found += 1 found += 1
end end
end end
end end
if found == 0 if found == 0
@stats.record_no_tag(filename, source_path) @stats.record_no_tag(filename, source_path)
elsif found == 1 elsif found == 1
@stats.record_tag(filename) @stats.record_tag(filename)
else else
raise("File contains #{found} license lines: #{source_path}") raise("File contains #{found} license lines: #{source_path}")
end end
end end
# This file matches at least one of the file-matching strings, and does not # This file matches at least one of the file-matching strings, and does not
# match any exceptions. Replace the magic string with the license text. # match any exceptions. Replace the magic string with the license text.
# #
def copy_file_with_license(source_path, target_path, filename) def copy_file_with_license(source_path, target_path, filename)
found = 0 found = 0
File.open(source_path) do |source_file| File.open(source_path) do |source_file|
File.open(target_path, "w") do |target_file| File.open(target_path, "w") do |target_file|
source_file.each do |line| source_file.each do |line|
if line.include?(MAGIC_STRING) if line.include?(MAGIC_STRING)
found += 1 found += 1
insert_license_text(target_file, line) insert_license_text(target_file, line)
else else
target_file.print line target_file.print line
end end
end end
end end
end end
if found == 0 if found == 0
@stats.record_no_tag(filename, source_path) @stats.record_no_tag(filename, source_path)
elsif found == 1 elsif found == 1
@stats.record_tag(filename) @stats.record_tag(filename)
else else
raise("File contains #{found} license lines: #{source_path}") raise("File contains #{found} license lines: #{source_path}")
end end
end end
# Figure out the comment characters and write the license text to the file. # Figure out the comment characters and write the license text to the file.
# #
def insert_license_text(target_file, line) def insert_license_text(target_file, line)
ends = line.split(MAGIC_STRING) ends = line.split(MAGIC_STRING)
if ends.size != 2 if ends.size != 2
raise ("Can't parse this license line: #{line}") raise ("Can't parse this license line: #{line}")
end end
target_file.print "#{ends[0].strip}\n" target_file.print "#{ends[0].strip}\n"
@license_text.each do |text| @license_text.each do |text|
target_file.print "#{text.rstrip}\n" target_file.print "#{text.rstrip}\n"
end end
target_file.print "#{ends[1].strip}\n" target_file.print "#{ends[1].strip}\n"
end end
# This file either doesn't match any of the file-matching strings, or # This file either doesn't match any of the file-matching strings, or
# matches an exception # matches an exception
# #
def copy_file_without_license(source_path, target_path) def copy_file_without_license(source_path, target_path)
FileUtils.cp(source_path, target_path) FileUtils.cp(source_path, target_path)
end end
# ------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------
public public
# ------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------
# Setup and get ready to process. # Setup and get ready to process.
# #
# * properties is a map of keys to values, probably parsed from a properties file. # * properties is a map of keys to values, probably parsed from a properties file.
# #
def initialize(properties) def initialize(properties)
@scan_only_string = properties['scan_only'] @scan_only_string = properties['scan_only']
@scan_only = 'false' != @scan_only_string @scan_only = 'false' != @scan_only_string
@file_match_list = properties['file_matchers'] @file_match_list = properties['file_matchers']
@skip_directories_list = properties['skip_directories'] @skip_directories_list = properties['skip_directories']
@report_level_string = properties['report_level'] @report_level_string = properties['report_level']
# These properties contain paths, and if they are relative paths, they # These properties contain paths, and if they are relative paths, they
# should be relative to the properties file itself. # should be relative to the properties file itself.
@source_dir = relative_to_properties(properties, 'source_dir') @source_dir = relative_to_properties(properties, 'source_dir')
@target_dir = relative_to_properties(properties, 'target_dir') @target_dir = relative_to_properties(properties, 'target_dir')
# These properties contain paths, and if they are relative paths, they # These properties contain paths, and if they are relative paths, they
# should be relative to the source_directory. # should be relative to the source_directory.
@license_file = relative_to_source(properties, 'license_file') @license_file = relative_to_source(properties, 'license_file')
@known_exceptions_file = relative_to_source(properties, 'known_exceptions') @known_exceptions_file = relative_to_source(properties, 'known_exceptions')
sanity_checks_on_parameters() sanity_checks_on_parameters()
@full_report = @report_level_string === 'full' @full_report = @report_level_string === 'full'
@short_report = @report_level_string === 'short' @short_report = @report_level_string === 'short'
@file_matchers = @file_match_list.strip.split(/,\s*/) @file_matchers = @file_match_list.strip.split(/,\s*/)
@skip_directories = @skip_directories_list.strip.split(/,\s*/) @skip_directories = @skip_directories_list.strip.split(/,\s*/)
@license_text = prepare_license_text(@license_file) @license_text = prepare_license_text(@license_file)
@known_exceptions = prepare_exception_globs(@known_exceptions_file, @source_dir) @known_exceptions = prepare_exception_globs(@known_exceptions_file, @source_dir)
@stats = LicenserStats.new(@source_dir, @file_matchers, @full_report) @stats = LicenserStats.new(@source_dir, @file_matchers, @full_report)
end end
# Start the recursive scanning (and copying). # Start the recursive scanning (and copying).
def process() def process()
scan_dir('.', '.') scan_dir('.', '.')
end end
# Report the summary statistics # Report the summary statistics
def report(properties) def report(properties)
verb = @scan_only ? "scanned" : "copied" verb = @scan_only ? "scanned" : "copied"
if (@short_report) if (@short_report)
subs = 0 subs = 0
@stats.substitutions.each {|line| subs += line[1] } @stats.substitutions.each {|line| subs += line[1] }
known = 0 known = 0
@stats.known_exceptions.each {|line| known += line[1] } @stats.known_exceptions.each {|line| known += line[1] }
missing = 0 missing = 0
@stats.missing_tags.each {|line| missing += line[1] } @stats.missing_tags.each {|line| missing += line[1] }
puts "Licenser: #{verb} #{@stats.file_count} files in #{@stats.dir_count} directories." puts "Licenser: #{verb} #{@stats.file_count} files in #{@stats.dir_count} directories."
printf(" Substitutions: %5d\n", subs) printf(" Substitutions: %5d\n", subs)
printf(" Known exceptions: %5d\n", known) printf(" Known exceptions: %5d\n", known)
printf(" Missing tags: %5d\n", missing) printf(" Missing tags: %5d\n", missing)
else else
puts "Licenser: run completed at #{DateTime.now.strftime("%H:%M:%S on %b %d, %Y")}" puts "Licenser: run completed at #{DateTime.now.strftime("%H:%M:%S on %b %d, %Y")}"
puts " #{verb} #{@stats.file_count} files in #{@stats.dir_count} directories." puts " #{verb} #{@stats.file_count} files in #{@stats.dir_count} directories."
puts puts
puts 'Substitutions' puts 'Substitutions'
@stats.substitutions.sort.each do |line| @stats.substitutions.sort.each do |line|
printf("%5d %s\n", line[1], line[0]) printf("%5d %s\n", line[1], line[0])
end end
puts puts
puts 'Known non-licensed files' puts 'Known non-licensed files'
@stats.known_exceptions.sort.each do |line| @stats.known_exceptions.sort.each do |line|
printf("%5d %s\n", line[1], line[0]) printf("%5d %s\n", line[1], line[0])
end end
puts puts
puts 'Missing tags' puts 'Missing tags'
@stats.missing_tags.sort.each do |line| @stats.missing_tags.sort.each do |line|
printf("%5d %s\n", line[1], line[0]) printf("%5d %s\n", line[1], line[0])
end end
puts puts
puts 'properties:' puts 'properties:'
properties.each do |key, value| properties.each do |key, value|
puts " #{key} = #{value}" puts " #{key} = #{value}"
end end
end end
end end
# Were we successful or not? # Were we successful or not?
def success? def success?
return @stats.missing_tags.empty? return @stats.missing_tags.empty?
end end
end end
# #
# #
# ------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------
# Standalone calling. # Standalone calling.
# #
# Do this if this program was called from the command line. That is, if the command # Do this if this program was called from the command line. That is, if the command
# expands to the path of this file. # expands to the path of this file.
# ------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------
# #
if File.expand_path($0) == File.expand_path(__FILE__) if File.expand_path($0) == File.expand_path(__FILE__)
if ARGV.length == 0 if ARGV.length == 0
raise("No arguments - usage is: ruby licenser.rb <properties_file>") raise("No arguments - usage is: ruby licenser.rb <properties_file>")
end end
if !File.file?(ARGV[0]) if !File.file?(ARGV[0])
raise "File does not exist: '#{ARGV[0]}'." raise "File does not exist: '#{ARGV[0]}'."
end end
properties = PropertyFileReader.read(ARGV[0]) properties = PropertyFileReader.read(ARGV[0])
l = Licenser.new(properties) l = Licenser.new(properties)
l.process l.process
l.report(properties) l.report(properties)
if l.success? if l.success?
puts "Licenser was successful." puts "Licenser was successful."
exit 0 exit 0
else else
puts "Licenser found problems." puts "Licenser found problems."
exit 1 exit 1
end end
end end

View file

@ -1,95 +1,95 @@
=begin =begin
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
Collect the statistics of a licenser run. Collect the statistics of a licenser run.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
=end =end
class LicenserStats class LicenserStats
attr_reader :substitutions attr_reader :substitutions
attr_reader :missing_tags attr_reader :missing_tags
attr_reader :known_exceptions attr_reader :known_exceptions
attr_reader :file_count attr_reader :file_count
attr_reader :dir_count attr_reader :dir_count
# ------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------
private private
# ------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------
# #
def which_match(filename) def which_match(filename)
@file_matchers.each do |matcher| @file_matchers.each do |matcher|
return matcher if File.fnmatch(matcher, filename) return matcher if File.fnmatch(matcher, filename)
end end
raise("filename matches no matchers!: #{filename}") raise("filename matches no matchers!: #{filename}")
end end
# ------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------
public public
# ------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------
def initialize(root_dir, file_matchers, full) def initialize(root_dir, file_matchers, full)
@root_dir = "#{root_dir}/".gsub('//', '/') @root_dir = "#{root_dir}/".gsub('//', '/')
@file_matchers = file_matchers @file_matchers = file_matchers
@full = full @full = full
# keep track of how many substitutions for all file types # keep track of how many substitutions for all file types
@substitutions = Hash.new() @substitutions = Hash.new()
file_matchers.each do |matcher| file_matchers.each do |matcher|
@substitutions[matcher] = 0 @substitutions[matcher] = 0
end end
# keep track of missing tags, only in file types that have missing tags # keep track of missing tags, only in file types that have missing tags
@missing_tags = Hash.new(0) @missing_tags = Hash.new(0)
# keep track of how many known non-licensed files we encounter, and what types. # keep track of how many known non-licensed files we encounter, and what types.
@known_exceptions = Hash.new(0) @known_exceptions = Hash.new(0)
# keep track of how many files are copied # keep track of how many files are copied
@file_count = 0 @file_count = 0
#keep track of how many directories are copied #keep track of how many directories are copied
@dir_count = 0 @dir_count = 0
end end
def enter_directory(path) def enter_directory(path)
@dir_count += 1 @dir_count += 1
puts "Entering directory: #{path}" if @full puts "Entering directory: #{path}" if @full
end end
def record_scan_non_matching(filename) def record_scan_non_matching(filename)
@file_count += 1 @file_count += 1
puts " Scan without mods: #{filename}" if @full puts " Scan without mods: #{filename}" if @full
end end
def record_copy_non_matching(filename) def record_copy_non_matching(filename)
@file_count += 1 @file_count += 1
puts " Copy without mods: #{filename}" if @full puts " Copy without mods: #{filename}" if @full
end end
def record_scan_matching(filename) def record_scan_matching(filename)
@file_count += 1 @file_count += 1
puts " Scan with mods: #{filename}" if @full puts " Scan with mods: #{filename}" if @full
end end
def record_copy_matching(filename) def record_copy_matching(filename)
@file_count += 1 @file_count += 1
puts " Copy with mods: #{filename}" if @full puts " Copy with mods: #{filename}" if @full
end end
def record_known_exception(filename) def record_known_exception(filename)
@file_count += 1 @file_count += 1
puts " Known exception: #{filename}" if @full puts " Known exception: #{filename}" if @full
@known_exceptions[which_match(filename)] += 1 @known_exceptions[which_match(filename)] += 1
end end
def record_tag(filename) def record_tag(filename)
puts " Substituted license text into #{filename}" if @full puts " Substituted license text into #{filename}" if @full
@substitutions[which_match(filename)] += 1 @substitutions[which_match(filename)] += 1
end end
def record_no_tag(filename, source_path) def record_no_tag(filename, source_path)
puts "ERROR: Found no license tag in #{source_path.sub(@root_dir, '')}" puts "ERROR: Found no license tag in #{source_path.sub(@root_dir, '')}"
@missing_tags[which_match(filename)] += 1 @missing_tags[which_match(filename)] += 1
end end
end end

View file

@ -1,37 +1,37 @@
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
# Properties for running the licenser utility in Vitro core. # Properties for running the licenser utility in Vitro core.
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
# The path to the top level directory to be scanned or copied # The path to the top level directory to be scanned or copied
# (if relative, then relative to this file) # (if relative, then relative to this file)
source_dir = ../../../ source_dir = ../../../
# The path to the top level directory to copy into (ignored if only scanning) # The path to the top level directory to copy into (ignored if only scanning)
# (if relative, then relative to this file) # (if relative, then relative to this file)
target_dir = target_dir =
# A list of filename globs that match the files we want to license, # A list of filename globs that match the files we want to license,
# delimited by commas with optional white-space. # delimited by commas with optional white-space.
file_matchers = *.java, *.jsp, *.tld, *.xsl, *.xslt, *.css, *.js, *.ftl, *.xml file_matchers = *.java, *.jsp, *.tld, *.xsl, *.xslt, *.css, *.js, *.ftl, *.xml
# "globs" that describe paths that we won't follow for scanning OR FOR COPYING. # "globs" that describe paths that we won't follow for scanning OR FOR COPYING.
# (relative to the source_dir) # (relative to the source_dir)
skip_directories = ./bin, ./.svn, ./**/.svn, ./webapp/.build skip_directories = ./bin, ./.svn, ./**/.svn, ./webapp/.build
# The path to a file containing filename/path globs that match the files that # The path to a file containing filename/path globs that match the files that
# we know should have no license tags in them. # we know should have no license tags in them.
# The file contains one glob per line; blank lines and comments ("#") are ignored. # The file contains one glob per line; blank lines and comments ("#") are ignored.
# (if relative, then relative to the source directory) # (if relative, then relative to the source directory)
known_exceptions = webapp/config/licenser/known_exceptions.txt known_exceptions = webapp/config/licenser/known_exceptions.txt
# The path to the text of the license agreement (ignored if only scanning) # The path to the text of the license agreement (ignored if only scanning)
# If the agreement contains a ${year} token, the current year will be substituted. # If the agreement contains a ${year} token, the current year will be substituted.
# (if relative, then relative to the source directory) # (if relative, then relative to the source directory)
license_file = doc/license.txt license_file = doc/license.txt
# Set to 'full' for a full report, 'short' for a brief statment, or to anything # Set to 'full' for a full report, 'short' for a brief statment, or to anything
# else for a medium-length summary. # else for a medium-length summary.
report_level = short report_level = short
# if true, we are just scanning, not copying. # if true, we are just scanning, not copying.
scan_only = true scan_only = true