changeset 2:c6ca0a26d93e

initial implementation of agent-side symbol indexer
author Evgeniy.Koshkin
date Thu, 18 Jul 2013 15:34:28 +0400
parents 33e8cf5b2d57
children 53028ba390e0
files agent/agent.iml agent/src/META-INF/build-agent-plugin-symbol-server.xml agent/src/jetbrains/buildServer/symbols/SymbolsIndexer.java common/src/jetbrains/buildServer/symbols/SymbolsConstants.java server/server.iml server/src/META-INF/build-server-plugin-symbol-server.xml server/src/jetbrains/buildServer/symbols/IndexSymbolsBuildFeature.java
diffstat 7 files changed, 172 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/agent/agent.iml	Tue Jul 16 20:17:11 2013 +0400
+++ b/agent/agent.iml	Thu Jul 18 15:34:28 2013 +0400
@@ -8,6 +8,7 @@
           <file>jar://$TeamCityDistribution$/buildAgent/lib/agent.jar!/META-INF/buildAgentSpring.xml</file>
           <file>jar://$TeamCityDistribution$/buildAgent/lib/agent.jar!/META-INF/buildAgentPlugins.xml</file>
           <file>file://$MODULE_DIR$/fake-teamcity-agent-plugin-context.xml</file>
+          <file>file://$MODULE_DIR$/src/META-INF/build-agent-plugin-symbol-server.xml</file>
         </fileset>
       </configuration>
     </facet>
@@ -24,6 +25,7 @@
     <orderEntry type="library" name="log4j" level="project" />
     <orderEntry type="library" name="Idea-OpenApi" level="project" />
     <orderEntry type="module" module-name="common" />
+    <orderEntry type="library" name="TeamCity agent runtime" level="project" />
   </component>
 </module>
 
--- a/agent/src/META-INF/build-agent-plugin-symbol-server.xml	Tue Jul 16 20:17:11 2013 +0400
+++ b/agent/src/META-INF/build-agent-plugin-symbol-server.xml	Thu Jul 18 15:34:28 2013 +0400
@@ -6,12 +6,6 @@
         default-autowire="constructor"
         >
 
-  <!-- declare all beans you like Spring Dependency Injection to create -->
-  <!-- see http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html -->
-
-  <!-- sample: 
-  <bean class="bean class fully qualified name"/>
-  -->
-
+  <bean class="jetbrains.buildServer.symbols.SymbolsIndexer"/>
 
 </beans>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/src/jetbrains/buildServer/symbols/SymbolsIndexer.java	Thu Jul 18 15:34:28 2013 +0400
@@ -0,0 +1,130 @@
+package jetbrains.buildServer.symbols;
+
+import com.intellij.execution.configurations.GeneralCommandLine;
+import jetbrains.buildServer.ExecResult;
+import jetbrains.buildServer.SimpleCommandLineProcessRunner;
+import jetbrains.buildServer.agent.AgentLifeCycleAdapter;
+import jetbrains.buildServer.agent.AgentLifeCycleListener;
+import jetbrains.buildServer.agent.AgentRunningBuild;
+import jetbrains.buildServer.agent.BuildFinishedStatus;
+import jetbrains.buildServer.agent.artifacts.ArtifactsWatcher;
+import jetbrains.buildServer.agent.impl.artifacts.ArtifactsBuilderAdapter;
+import jetbrains.buildServer.agent.impl.artifacts.ArtifactsCollection;
+import jetbrains.buildServer.agent.plugins.beans.PluginDescriptor;
+import jetbrains.buildServer.util.EventDispatcher;
+import jetbrains.buildServer.util.FileUtil;
+import org.apache.log4j.Logger;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * @author Evgeniy.Koshkin
+ */
+public class SymbolsIndexer extends ArtifactsBuilderAdapter {
+
+  public static final Logger LOG = Logger.getLogger(SymbolsIndexer.class);
+
+  public static final String PDB_FILE_EXTENSION = "pdb";
+  public static final String EXE_FILE_EXTENSION = "exe";
+  public static final String DLL_FILE_EXTENSION = "dll";
+
+  public static final String SYMBOLS_EXE = "symbols.exe";
+  public static final String DUMP_SYMBOL_SIGN_CMD = "dumpSymbolSign";
+  public static final String DUMP_BIN_SIGN_CMD = "dumpBinSign";
+
+  @NotNull private final ArtifactsWatcher myArtifactsWatcher;
+  @NotNull private final File myNativeToolPath;
+  @Nullable private AgentRunningBuild myBuild;
+  @Nullable private Collection<File> myBinariesToProcess;
+  @Nullable private Collection<File> mySymbolsToProcess;
+
+  public SymbolsIndexer(@NotNull final PluginDescriptor pluginDescriptor, @NotNull final EventDispatcher<AgentLifeCycleListener> agentDispatcher, @NotNull final ArtifactsWatcher artifactsWatcher) {
+    myNativeToolPath = new File(new File(pluginDescriptor.getPluginRoot(), "bin"), SYMBOLS_EXE);
+    myArtifactsWatcher = artifactsWatcher;
+    agentDispatcher.addListener(new AgentLifeCycleAdapter() {
+      @Override
+      public void buildStarted(@NotNull final AgentRunningBuild runningBuild) {
+        myBuild = runningBuild;
+        myBinariesToProcess = new HashSet<File>();
+        mySymbolsToProcess = new HashSet<File>();
+      }
+
+      @Override
+      public void beforeBuildFinish(@NotNull AgentRunningBuild build, @NotNull BuildFinishedStatus buildStatus) {
+        super.beforeBuildFinish(build, buildStatus);
+        if (myBuild == null || mySymbolsToProcess == null || myBinariesToProcess == null) return;
+        if (mySymbolsToProcess.isEmpty()) {
+          LOG.debug("Symbols weren't found in artifacts to be published.");
+        } else {
+          final File targetDir = myBuild.getBuildTempDirectory();
+          try {
+            final File symbolSignaturesFile = dumpSymbolSignatures(mySymbolsToProcess, targetDir);
+            myArtifactsWatcher.addNewArtifactsPath(symbolSignaturesFile + "=>" + ".teamcity/symbols/symbol-signatures.xml");
+            final File binariesSignaturesFile = dumpBinarySignatures(myBinariesToProcess, targetDir);
+            myArtifactsWatcher.addNewArtifactsPath(binariesSignaturesFile + "=>" + ".teamcity/symbols/binary-signatures.xml");
+          } catch (IOException e) {
+            LOG.error("Error while dumping symbols/binaries signatures.", e);
+          }
+        }
+        mySymbolsToProcess = null;
+        myBinariesToProcess = null;
+        myBuild = null;
+      }
+    });
+  }
+
+  @Override
+  public void afterCollectingFiles(@NotNull List<ArtifactsCollection> artifacts) {
+    super.afterCollectingFiles(artifacts);
+    if(myBuild == null || mySymbolsToProcess == null || myBinariesToProcess == null) return;
+    if(myBuild.getBuildFeaturesOfType(SymbolsConstants.BUILD_FEATURE_TYPE).isEmpty()){
+      LOG.debug(SymbolsConstants.BUILD_FEATURE_TYPE + " build feature disabled. No indexing performed.");
+      return;
+    }
+    LOG.debug(SymbolsConstants.BUILD_FEATURE_TYPE + " build feature enabled. Searching for symbol files.");
+    mySymbolsToProcess.addAll(getArtifactPathsByFileExtension(artifacts, PDB_FILE_EXTENSION));
+    myBinariesToProcess.addAll(getArtifactPathsByFileExtension(artifacts, EXE_FILE_EXTENSION));
+    myBinariesToProcess.addAll(getArtifactPathsByFileExtension(artifacts, DLL_FILE_EXTENSION));
+  }
+
+  private Collection<File> getArtifactPathsByFileExtension(List<ArtifactsCollection> artifactsCollections, String fileExtension){
+    final Collection<File> result = new HashSet<File>();
+    for(ArtifactsCollection artifactsCollection : artifactsCollections){
+      if(artifactsCollection.isEmpty()) continue;
+      for (File artifact : artifactsCollection.getFilePathMap().keySet()){
+        if(FileUtil.getExtension(artifact.getPath()).equalsIgnoreCase(fileExtension))
+          result.add(artifact);
+      }
+    }
+    return result;
+  }
+
+  private File dumpSymbolSignatures(Collection<File> files, File targetDir) throws IOException {
+    final File tempFile = FileUtil.createTempFile(targetDir, "symbol-signatures-", ".xml", false);
+    runTool(DUMP_SYMBOL_SIGN_CMD, files, tempFile);
+    return tempFile;
+  }
+
+  private File dumpBinarySignatures(Collection<File> files, File targetDir) throws IOException {
+    final File tempFile = FileUtil.createTempFile(targetDir, "binary-signatures-", ".xml", false);
+    runTool(DUMP_BIN_SIGN_CMD, files, tempFile);
+    return tempFile;
+  }
+
+  private void runTool(String cmd, Collection<File> files, File output){
+    final GeneralCommandLine commandLine = new GeneralCommandLine();
+    commandLine.setExePath(myNativeToolPath.getPath());
+    commandLine.addParameter(cmd);
+    commandLine.addParameter(String.format("/o=\"%s\"", output.getPath()));
+    for(File file : files){
+      commandLine.addParameter(file.getPath());
+    }
+    final ExecResult execResult = SimpleCommandLineProcessRunner.runCommand(commandLine, null);
+    if (execResult.getExitCode() == 0) return;
+    LOG.warn(String.format("%s ends with non-zero exit code.", SYMBOLS_EXE));
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/jetbrains/buildServer/symbols/SymbolsConstants.java	Thu Jul 18 15:34:28 2013 +0400
@@ -0,0 +1,8 @@
+package jetbrains.buildServer.symbols;
+
+/**
+ * @author Evgeniy.Koshkin
+ */
+public class SymbolsConstants {
+  public static final String BUILD_FEATURE_TYPE = "symbol-indexer";
+}
--- a/server/server.iml	Tue Jul 16 20:17:11 2013 +0400
+++ b/server/server.iml	Thu Jul 18 15:34:28 2013 +0400
@@ -18,6 +18,7 @@
           <file>jar://$TeamCityDistribution$/buildAgent/lib/common-impl.jar!/META-INF/per-plugin-shared-spring.xml</file>
           <file>jar://$TeamCityDistribution$/buildAgent/lib/common-impl.jar!/META-INF/plugin-model-shared-spring.xml</file>
           <file>jar://$TeamCityDistribution$/buildAgent/lib/common-impl.jar!/META-INF/shared-spring-sub-container.xml</file>
+          <file>file://$MODULE_DIR$/src/META-INF/build-server-plugin-symbol-server.xml</file>
         </fileset>
       </configuration>
     </facet>
--- a/server/src/META-INF/build-server-plugin-symbol-server.xml	Tue Jul 16 20:17:11 2013 +0400
+++ b/server/src/META-INF/build-server-plugin-symbol-server.xml	Thu Jul 18 15:34:28 2013 +0400
@@ -5,11 +5,7 @@
         xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
         default-autowire="constructor"
         >
-  <!-- declare all beans you like Spring Dependency Injection to create -->
-  <!-- see http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html -->
 
-  <!-- sample: 
-  <bean class="bean class fully qualified name"/>
-  -->
+  <bean class="jetbrains.buildServer.symbols.IndexSymbolsBuildFeature"/>
 
 </beans>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/src/jetbrains/buildServer/symbols/IndexSymbolsBuildFeature.java	Thu Jul 18 15:34:28 2013 +0400
@@ -0,0 +1,29 @@
+package jetbrains.buildServer.symbols;
+
+import jetbrains.buildServer.serverSide.BuildFeature;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Evgeniy.Koshkin
+ */
+public class IndexSymbolsBuildFeature extends BuildFeature {
+
+  @NotNull
+  @Override
+  public String getType() {
+    return SymbolsConstants.BUILD_FEATURE_TYPE;
+  }
+
+  @NotNull
+  @Override
+  public String getDisplayName() {
+    return "Symbol files (.pdb) indexer";
+  }
+
+  @Nullable
+  @Override
+  public String getEditParametersUrl() {
+    return null;
+  }
+}