From f93796cd53c49c82334bb4438564b09fe8b73aa2 Mon Sep 17 00:00:00 2001 From: Jason Tsay Date: Thu, 9 Jun 2011 16:17:01 -0700 Subject: [PATCH] Add per-project prolog submit rule files When loading the prolog environment, now checks refs/meta/config branch for a file called rules.pl. If it exists, consult the file. Expects a predicate called submit_rule. If no file is found, uses the default_submit predicate in common_rules.pl. Change-Id: Ia76d0dc5aba67cf3c68c56589fe013c63157bca5 --- .../gerrit/server/git/ProjectConfig.java | 13 +++++++ .../gerrit/server/project/ChangeControl.java | 35 ++++++++++++------- .../gerrit/server/project/ProjectState.java | 26 ++++++++++++-- gerrit-server/src/main/prolog/common_rules.pl | 26 +++++++++++++- 4 files changed, 85 insertions(+), 15 deletions(-) diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java index 96eacfeb7c..6f022d3214 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java @@ -72,6 +72,7 @@ public class ProjectConfig extends VersionedMetaData { private Map groupsByUUID; private Map accessSections; private List validationErrors; + private String prologRules; public static ProjectConfig read(MetaDataUpdate update) throws IOException, ConfigInvalidException { @@ -148,6 +149,17 @@ public class ProjectConfig extends VersionedMetaData { return groupsByUUID.get(uuid); } + /** + * @return the project's Prolog based rules.pl script, + * if present in the branch. Null if there are no rules. + */ + public String getPrologRules() { + if (prologRules.equals("")) { + return null; + } + return prologRules; + } + /** * Check all GroupReferences use current group name, repairing stale ones. * @@ -188,6 +200,7 @@ public class ProjectConfig extends VersionedMetaData { protected void onLoad() throws IOException, ConfigInvalidException { Map groupsByName = readGroupList(); + prologRules = readUTF8("rules.pl"); Config rc = readConfig(PROJECT_CONFIG); project = new Project(projectName); diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java index 7a3b8485be..32794a853f 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java @@ -28,10 +28,10 @@ import com.google.gwtorm.client.OrmException; import com.google.inject.Inject; import com.google.inject.Provider; +import com.googlecode.prolog_cafe.compiler.CompileException; import com.googlecode.prolog_cafe.lang.IntegerTerm; import com.googlecode.prolog_cafe.lang.PrologException; import com.googlecode.prolog_cafe.lang.StructureTerm; -import com.googlecode.prolog_cafe.lang.SymbolTerm; import com.googlecode.prolog_cafe.lang.Term; import com.googlecode.prolog_cafe.lang.VariableTerm; @@ -252,25 +252,36 @@ public class ChangeControl { return result; } - PrologEnvironment env = getProjectControl().getProjectState().newPrologEnvironment(); + PrologEnvironment env; + try { + env = getProjectControl().getProjectState().newPrologEnvironment(); + } catch (CompileException err) { + log.error("cannot consult rules.pl", err); + return new CanSubmitResult("Error reading submit rule"); + } + env.set(StoredValues.REVIEW_DB, db); env.set(StoredValues.CHANGE, change); env.set(StoredValues.PATCH_SET_ID, patchSetId); env.set(StoredValues.CHANGE_CONTROL, this); - StructureTerm submitRule = SymbolTerm.makeSymbol( - "com.google.gerrit.rules.common", "default_submit", 1); + Term submitRule = env.once("com.google.gerrit.rules.common", "locate_submit_rule", + new VariableTerm()); + if (submitRule == null) { + log.error("Error in locate_submit_rule: no submit_rule found"); + return new CanSubmitResult("Error in finding submit rule"); + } List results = new ArrayList(); try { for (Term[] template : env.all( - "com.google.gerrit.rules.common", "can_submit", - submitRule, - new VariableTerm())) { - results.add(template[1]); - } + "com.google.gerrit.rules.common", "can_submit", + submitRule, + new VariableTerm())) { + results.add(template[1]); + } } catch (PrologException err) { - log.error("PrologException calling "+submitRule, err); + log.error("PrologException calling " + submitRule, err); return new CanSubmitResult("Error in submit rule"); } @@ -319,7 +330,7 @@ public class ChangeControl { continue; } else if ("reject".equals(status.name())) { - return new CanSubmitResult("Submit blocked by "+ label); + return new CanSubmitResult("Submit blocked by " + label); } else if ("need".equals(status.name())) { if (status.isStructure() && status.arg(0).isInteger()) { @@ -340,4 +351,4 @@ public class ChangeControl { return CanSubmitResult.OK; } -} +} \ No newline at end of file diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java index 422230e44a..fe5c140a83 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java @@ -29,10 +29,17 @@ import com.google.gerrit.server.git.ProjectConfig; import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; +import com.googlecode.prolog_cafe.compiler.CompileException; +import com.googlecode.prolog_cafe.lang.JavaObjectTerm; +import com.googlecode.prolog_cafe.lang.Prolog; +import com.googlecode.prolog_cafe.lang.SymbolTerm; + import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; import java.io.IOException; +import java.io.PushbackReader; +import java.io.StringReader; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -121,9 +128,24 @@ public class ProjectState { } /** @return Construct a new PrologEnvironment for the calling thread. */ - public PrologEnvironment newPrologEnvironment() { + public PrologEnvironment newPrologEnvironment() throws CompileException { // TODO Replace this with a per-project ClassLoader to isolate rules. - return envFactory.create(getClass().getClassLoader()); + PrologEnvironment env = envFactory.create(getClass().getClassLoader()); + + //consult rules.pl at refs/meta/config branch for custom submit rules + String rules = getConfig().getPrologRules(); + if (rules != null) { + PushbackReader in = + new PushbackReader(new StringReader(rules), Prolog.PUSHBACK_SIZE); + JavaObjectTerm streamObject = new JavaObjectTerm(in); + if (!env.execute(Prolog.BUILTIN, "consult_stream", + SymbolTerm.makeSymbol("rules.pl"), streamObject)) { + throw new CompileException("Cannot consult rules.pl " + + getProject().getName() + " " + getConfig().getRevision()); + } + } + + return env; } public Project getProject() { diff --git a/gerrit-server/src/main/prolog/common_rules.pl b/gerrit-server/src/main/prolog/common_rules.pl index c3eeee394e..d083f87970 100644 --- a/gerrit-server/src/main/prolog/common_rules.pl +++ b/gerrit-server/src/main/prolog/common_rules.pl @@ -143,7 +143,7 @@ not_same(_, _). %% %% can_submit/2: %% -%% Execute the SubmitRule for each solution until one where all of the +%% Executes the SubmitRule for each solution until one where all of the %% states has the format label(_, ok(_)) is found, then cut away any %% remaining choice points leaving this as the last solution. %% @@ -162,6 +162,30 @@ is_all_ok([label(_, ok(__)) | Ls]) :- is_all_ok(Ls). is_all_ok(_) :- fail. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% locate_submit_rule/1: +%% +%% Finds a submit_rule depending on what rules are available. +%% If none are available, use default_submit/1. +%% +:- public locate_submit_rule/1. +%% + +locate_submit_rule(RuleName) :- + clause(user:submit_rule(_), _), + !, + RuleName = user:submit_rule + . +locate_submit_rule(RuleName) :- + '$compiled_predicate'(user, submit_rule, 1), + !, + RuleName = user:submit_rule + . +locate_submit_rule(RuleName) :- + RuleName = 'com.google.gerrit.rules.common':default_submit. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% default_submit/1: