Skip to content

Commit

Permalink
[JENKINS-18941] Support configuration of output folder in Builder and…
Browse files Browse the repository at this point in the history
… report folder in Publisher
  • Loading branch information
Maarten Los authored and rodrigc committed Dec 21, 2015
1 parent 8d3a37b commit b6ceff9
Show file tree
Hide file tree
Showing 10 changed files with 90 additions and 49 deletions.
Expand Up @@ -42,6 +42,7 @@ public class ClangScanBuildBuilder extends Builder{
private String scheme;
private String scanbuildargs;
private String xcodebuildargs;
private String outputFolderName;

@DataBoundConstructor
public ClangScanBuildBuilder(
Expand All @@ -53,7 +54,8 @@ public ClangScanBuildBuilder(
String workspace,
String scheme,
String scanbuildargs,
String xcodebuildargs){
String xcodebuildargs,
String outputFolderName){

this.target = Util.fixEmptyAndTrim( target );
this.targetSdk = Util.fixEmptyAndTrim( targetSdk );
Expand All @@ -64,6 +66,7 @@ public ClangScanBuildBuilder(
this.scheme = Util.fixEmptyAndTrim( scheme );
this.scanbuildargs = Util.fixEmptyAndTrim( scanbuildargs );
this.xcodebuildargs = Util.fixEmptyAndTrim( xcodebuildargs );
this.outputFolderName = Util.fixEmptyAndTrim( outputFolderName );
}

public String getClangInstallationName(){
Expand Down Expand Up @@ -98,6 +101,10 @@ public String getXcodebuildargs(){
return xcodebuildargs;
}

public String getOutputFolderName(){
return outputFolderName;
}

/**
* Removing slashes here in case the user adds a starting slash to the path.
*/
Expand Down Expand Up @@ -135,7 +142,7 @@ public boolean perform( @SuppressWarnings("rawtypes") AbstractBuild build, Launc
xcodebuild.setConfig( getConfig() );
xcodebuild.setAdditionalScanBuildArguments( getScanbuildargs() );
xcodebuild.setAdditionalXcodeBuildArguments( getXcodebuildargs() );
xcodebuild.setClangOutputFolder( new FilePath( build.getWorkspace(), ClangScanBuildUtils.REPORT_OUTPUT_FOLDERNAME) );
xcodebuild.setClangOutputFolder( new FilePath( build.getWorkspace(), getOutputFolderName()) );
xcodebuild.setWorkspace( getWorkspace() );
xcodebuild.setScheme( getScheme() );

Expand Down
Expand Up @@ -16,9 +16,9 @@ public static String getTransparentImagePath(){
return "/plugin/" + PluginImpl.SHORTNAME + "/transparent.png";
}

public static FilePath locateClangScanBuildReportFolder( AbstractBuild<?,?> build ){
public static FilePath locateClangScanBuildReportFolder( AbstractBuild<?,?> build, String folderName ){
if( build == null ) return null;
return new FilePath( new FilePath( build.getRootDir() ), REPORT_OUTPUT_FOLDERNAME );
return new FilePath( new FilePath( build.getRootDir() ), folderName );
}

}
Expand Up @@ -34,15 +34,18 @@ public class ClangScanBuildAction implements Action, StaplerProxy, ModelObject{
private FilePath bugSummaryXML;
private boolean markBuildUnstable;
private int bugCount;
private String outputFolderName;

private Pattern APPROVED_REPORT_REQUEST_PATTERN = Pattern.compile( "[^.\\\\/]*\\.html|StaticAnalyzer.*\\.html" );

public ClangScanBuildAction( AbstractBuild<?,?> build, int bugCount, boolean markBuildUnstable, int bugThreshold, FilePath bugSummaryXML ){
public ClangScanBuildAction( AbstractBuild<?,?> build, int bugCount, boolean markBuildUnstable,
int bugThreshold, FilePath bugSummaryXML, String outputFolderName ){
this.bugThreshold = bugThreshold;
this.bugCount = bugCount;
this.bugSummaryXML = bugSummaryXML;
this.markBuildUnstable = markBuildUnstable;
this.build = build;
this.outputFolderName = outputFolderName;
}

public AbstractBuild<?,?> build;
Expand Down Expand Up @@ -141,7 +144,7 @@ public void doBrowse( StaplerRequest req, StaplerResponse rsp ) throws IOExcepti
return;
}

FilePath reports = ClangScanBuildUtils.locateClangScanBuildReportFolder( build );
FilePath reports = ClangScanBuildUtils.locateClangScanBuildReportFolder( build, outputFolderName );
FilePath requestedFile = new FilePath( reports, trimFirstSlash( requestedPath ) );

try{
Expand Down
@@ -1,5 +1,6 @@
package jenkins.plugins.clangscanbuild.publisher;

import hudson.EnvVars;
import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
Expand Down Expand Up @@ -28,9 +29,9 @@
import jenkins.plugins.clangscanbuild.history.ClangScanBuildBugSummary;

public class ClangScanBuildPublisher extends Recorder{

private static final Logger LOGGER = Logger.getLogger( ClangScanBuildPublisher.class.getName() );

@Extension
public static final ClangScanBuildPublisherDescriptor DESCRIPTOR = new ClangScanBuildPublisherDescriptor();

Expand All @@ -45,25 +46,28 @@ public class ClangScanBuildPublisher extends Recorder{

private int bugThreshold;
private String clangexcludedpaths;
private String reportFolderName;

private boolean markBuildUnstableWhenThresholdIsExceeded;

public ClangScanBuildPublisher(
boolean markBuildUnstableWhenThresholdIsExceeded,
int bugThreshold,
String clangexcludedpaths
String clangexcludedpaths,
String reportFolderName
){

super();
this.markBuildUnstableWhenThresholdIsExceeded = markBuildUnstableWhenThresholdIsExceeded;
this.bugThreshold = bugThreshold;
this.clangexcludedpaths = Util.fixNull(clangexcludedpaths);
this.reportFolderName = Util.fixNull(reportFolderName);
}

public int getBugThreshold() {
return bugThreshold;
}

public boolean isMarkBuildUnstableWhenThresholdIsExceeded(){
return markBuildUnstableWhenThresholdIsExceeded;
}
Expand All @@ -76,6 +80,15 @@ public void setClangexcludedpaths(String clangExcludePaths){
this.clangexcludedpaths = Util.fixNull(clangExcludePaths);
}

public void setReportFolderName(String folderName){
this.reportFolderName = Util.fixNull(folderName);
}

public String getReportFolderName(){
return reportFolderName;
}


@Override
public Action getProjectAction( AbstractProject<?, ?> project ){
return new ClangScanBuildProjectAction( project );
Expand All @@ -85,7 +98,7 @@ public Action getProjectAction( AbstractProject<?, ?> project ){
public ClangScanBuildPublisherDescriptor getDescriptor() {
return DESCRIPTOR;
}

public BuildStepMonitor getRequiredMonitorService() {
return BuildStepMonitor.NONE;
}
Expand All @@ -98,19 +111,23 @@ public String getClangexcludedpaths(){
public boolean perform( AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener ) throws InterruptedException, IOException {

listener.getLogger().println( "Publishing Clang scan-build results" );

FilePath reportOutputFolder = new FilePath(build.getWorkspace(), ClangScanBuildUtils.REPORT_OUTPUT_FOLDERNAME);
FilePath reportMasterOutputFolder = ClangScanBuildUtils.locateClangScanBuildReportFolder(build);


// Expand build variables in the reportFolderName
EnvVars env = build.getEnvironment(listener);
String expandedReportFolderName = env.expand(reportFolderName);

FilePath reportOutputFolder = new FilePath(build.getWorkspace(), expandedReportFolderName);
FilePath reportMasterOutputFolder = ClangScanBuildUtils.locateClangScanBuildReportFolder(build, expandedReportFolderName);

// This copies the reports out of the generate date sub folder to the root of the reports folder and then deletes the clang generated folder
copyClangReportsOutOfGeneratedSubFolder( reportOutputFolder, listener );

// This copies the report dir to master
copyClangReportsToMaster( reportOutputFolder, reportMasterOutputFolder, listener );

// this digs into the clang results looking for the subfolder created by clang
List<FilePath> clangReports = locateClangBugReports( reportOutputFolder );

// this loads the previous bug summary for the last build. it is need to identify bugs added since last build
ClangScanBuildBugSummary previousBugSummary = getBugSummaryForLastBuild( build );

Expand Down Expand Up @@ -138,15 +155,15 @@ public boolean perform( AbstractBuild<?, ?> build, Launcher launcher, BuildListe
newBugSummary.add( bug );
}
}

// this line dumps a bugSummary.xml file to the build artifacts. did this instead of using job config xml for performance
//FilePath bugSummaryXMLFile = new FilePath( reportOutputFolder, "bugSummary.xml" );
FilePath bugSummaryXMLFile = new FilePath( new FilePath( build.getRootDir() ), "bugSummary.xml" );
String bugSummaryXML = AbstractBuild.XSTREAM.toXML( newBugSummary );
bugSummaryXMLFile.write( bugSummaryXML, "UTF-8" );

// this adds a build actions which records the bug count into the build results. This count is used to generate the trend charts
final ClangScanBuildAction action = new ClangScanBuildAction( build, newBugSummary.getBugCount(), markBuildUnstableWhenThresholdIsExceeded, bugThreshold, bugSummaryXMLFile );
final ClangScanBuildAction action = new ClangScanBuildAction( build, newBugSummary.getBugCount(), markBuildUnstableWhenThresholdIsExceeded, bugThreshold, bugSummaryXMLFile, expandedReportFolderName );
build.getActions().add( action );

// this checks if the build should be failed due to an increase in bugs
Expand All @@ -157,7 +174,7 @@ public boolean perform( AbstractBuild<?, ?> build, Launcher launcher, BuildListe

return true;
}

private ClangScanBuildBug createBugFromClangScanBuildHtml( String projectName, FilePath report, ClangScanBuildBugSummary previousBugSummary, String workspacePath ) throws InterruptedException {
ClangScanBuildBug bug = createBugInstance( projectName, report, workspacePath );

Expand Down Expand Up @@ -199,13 +216,13 @@ private void copyClangReportsOutOfGeneratedSubFolder( FilePath reportsFolder, Bu
listener.getLogger().println( "Could not locate a unique scan-build output folder in: " + reportsFolder );
return;
}
for (FilePath clangDateFolder : subFolders) {
clangDateFolder.copyRecursiveTo( reportsFolder );
clangDateFolder.deleteRecursive();

for (FilePath clangDataFolder : subFolders) {
clangDataFolder.copyRecursiveTo( reportsFolder );
clangDataFolder.deleteRecursive();
}
}catch( Exception e ){
listener.fatalError( "Unable to copy Clan scan-build output to build archive folder." );
listener.fatalError( "Unable to copy Clang scan-build output (" + reportsFolder + ") to build archive folder." );
}
}
/**
Expand All @@ -230,7 +247,7 @@ private ClangScanBuildBug createBugInstance( String projectName, FilePath report
ClangScanBuildBug instance = new ClangScanBuildBug();
String basePath = "StaticAnalyzer/";
String reportPath = report.getRemote();

int baseIdx = reportPath.indexOf(basePath);
if(baseIdx > -1) {
reportPath = reportPath.substring(baseIdx, reportPath.length());
Expand All @@ -239,7 +256,7 @@ private ClangScanBuildBug createBugInstance( String projectName, FilePath report
}

instance.setReportFile( reportPath );//( report.getName() );

String contents = null;
try {
// this code digs into the HTML report content to locate the bug markers using regex
Expand All @@ -260,7 +277,7 @@ private ClangScanBuildBug createBugInstance( String projectName, FilePath report
if( position >= 0 ){
sourceFile = sourceFile.substring( position + workspacePath.length() );
}

instance.setSourceFile( sourceFile );
}catch( IOException e ){
LOGGER.log( Level.ALL, "Unable to read file or locate clang markers in content: " + report );
Expand All @@ -276,7 +293,7 @@ private String getMatch( Pattern pattern, String contents ){
}
return null;
}

/**
* This locates all the generated HTML bug reports from scan-build and returns them as a list.
*/
Expand All @@ -286,5 +303,5 @@ protected List<FilePath> locateClangBugReports( FilePath clangOutputFolder ) thr
files.addAll( Arrays.asList( clangOutputFolder.list( "**/report-*.html" ) ) );
return files;
}

}
@@ -1,5 +1,6 @@
package jenkins.plugins.clangscanbuild.publisher;

import jenkins.plugins.clangscanbuild.ClangScanBuildUtils;
import net.sf.json.JSONObject;

import org.kohsuke.stapler.StaplerRequest;
Expand All @@ -15,24 +16,27 @@ public ClangScanBuildPublisherDescriptor(){
super( ClangScanBuildPublisher.class );
load();
}

@Override
public Publisher newInstance(StaplerRequest arg0, JSONObject json ) throws hudson.model.Descriptor.FormException {

boolean markBuildUnstable = false;
int bugThreshold = 0;
String excludedPaths = "";

String reportFolderName = ClangScanBuildUtils.REPORT_OUTPUT_FOLDERNAME;

JSONObject failWhenThresholdExceeded = json.optJSONObject( "failWhenThresholdExceeded" );
if( failWhenThresholdExceeded != null ){
markBuildUnstable = true;
bugThreshold = failWhenThresholdExceeded.getInt( "bugThreshold" );
excludedPaths = failWhenThresholdExceeded.getString( "clangexcludedpaths" );
}

return new ClangScanBuildPublisher( markBuildUnstable, bugThreshold, excludedPaths );

reportFolderName = json.getString("reportFolderName");

return new ClangScanBuildPublisher( markBuildUnstable, bugThreshold, excludedPaths, reportFolderName );
}

@Override
public String getDisplayName() {
return "Publish Clang Scan-Build Results";
Expand Down
Expand Up @@ -43,5 +43,9 @@
</f:entry>

</f:advanced>

<f:entry title="Output Folder" field="outputFolderName">
<f:textbox default="clangScanBuildReports"/>
</f:entry>

</j:jelly>
Expand Up @@ -13,6 +13,11 @@
</f:entry>

</f:optionalBlock>

<f:entry title="Report folder" field="reportFolderName">
<f:textbox default="clangScanBuildReports"/>
</f:entry>

</table>
</f:nested>

Expand Down
Expand Up @@ -14,15 +14,16 @@ public void testRoundTripConfiguration() throws Exception{

FreeStyleProject p = createFreeStyleProject();

ClangScanBuildBuilder builderBefore = new ClangScanBuildBuilder( "target", "sdk", "config", "installName", "projPath", "workspace", "scheme", "someargs", "somexcodeargs" );
ClangScanBuildBuilder builderBefore = new ClangScanBuildBuilder( "target", "sdk", "config",
"installName", "projPath", "workspace", "scheme", "someargs", "somexcodeargs", "someoutputfoldername" );
p.getBuildersList().add( builderBefore );

HtmlForm form = createWebClient().getPage( p, "configure" ).getFormByName( "config" );
submit( form );

ClangScanBuildBuilder builderAfter = p.getBuildersList().get( ClangScanBuildBuilder.class );

assertEqualBeans( builderBefore, builderAfter, "target,config,targetSdk,xcodeProjectSubPath,workspace,scheme,scanbuildargs,xcodebuildargs" );
assertEqualBeans( builderBefore, builderAfter, "target,config,targetSdk,xcodeProjectSubPath,workspace,scheme,scanbuildargs,xcodebuildargs,outputFolderName" );
}

}

0 comments on commit b6ceff9

Please sign in to comment.