Showing posts with label perforce. Show all posts
Showing posts with label perforce. Show all posts

Wednesday, April 18, 2012

Useful regular expression: how to verify perforce branch path

Here is the regular expression (aka regexp) pattern to verify if the input string matches the rules of perforce branch specification (the string should look like this: //depot/level1/level2/level3/... )
//(?:\w+?/)+?(?:\.\.\.$)
 Symbols ?:  mean that we would not like to capture them (does not make sense for match function of regular expression but still reasonable to make the logic of the pattern clear)/ 

Tuesday, April 17, 2012

ANT example on how to get local disk path of perforce branch

This example will show how to combine batch scripting practice with ant scripting one to make you capable to operate with the local disk paths of the branches of perforce version control system. This will also highlight the example of how to use marcrodef in ANT.

So to be able to fetch branch local path from perforce you have to know the following

  • which perforce server and port you're going to connect to
  • which credentials
  • the client you'd like to use (different clients may map the one branch to different places on your hard drive)
  • Finally the path you would like to convert

So manually the operation will look like this:
>p4 -p<server_name>:<server-port> -u<user_name> -P<password> -c<client_name> where <branch_or_file>

this command requests the server and returns the following output:
//depot/your_branch //YOUR-CLIENT/your_branch YOUR-CLIENT-ROOT\local_path_of_your_branch 
This is actually the problem preventing us from using the only ant to run it as the information we need resides at the last token of the string (YOUR-CLIENT-ROOT\local_path_of_your_branch)
That's why I use batch scripting here. The batch will take the configured command as the input, parse it and return only required part. This is achieved by a simple script (let it be named parse-p4-output.bat) that is actually represented with single line

FOR /F "tokens=3 delims= " %%A IN ('p4 -p%1:%2 -u%3 -P%4 -c%5 where %6') DO ECHO.%%A

For /F command breaks the output of the command from parentheses into set of tokens using the delimiter specified after delims= pattern. We need the third one so the command hods the following structure (taking into account that the delimiter is just the single white-space): "tokens=3 delims= "

Lets now switch to ant. To make the usage of this functionality convenient we're wrapping it with so called macrodef. Once it is defined it can be used wherever in the script in very simple way.

So, here is how it should look like. Somewhere in the ANT script we'd like to call the instruction like this:

<get-disk-path 
 path="//depot/your_branch" 
 save-to="save.to.prop"
 p4-host="your-server"
 p4-port="your-port"
 p4-user="your-user"
 p4-pswd="you-password"
 p4-clnt="YOUR-CLIENT"
/>

Here you see the obvious set of attributes. Basically the usage of such the macrodef (syntactically) does not differ from the regular ant task. The only comment for that is that after the instruction has been called the property save.to.prop will be holding the required output (and definitely you may specify your own).

Below is the definition of such the macrodef. You should describe it out of any target right under the project element.

<macrodef name="get-disk-path">
    <attribute name="p4-host"/>
    <attribute name="p4-port"/>
    <attribute name="p4-user"/>
    <attribute name="p4-pswd"/>
    <attribute name="p4-clnt"/>
    <attribute name="path"/>
    <attribute name="save-to"/>
    <sequential>
    <exec executable="parse-p4-output.bat" 
     outputproperty="@{save-to}"> 
     <arg value="@{p4-host}"/>
     <arg value="@{p4-port}"/>
     <arg value="@{p4-user}"/>
     <arg value="@{p4-pswd}"/>
     <arg value="@{p4-clnt}"/>
     <arg value="@{path}"/>
    </exec>
    </sequential>
</macrodef>

So this is how such the feature can be implemented. Inner code of macrodef calls the batch scenario. It parsed the command output and returns back to ANT. Then the output is placed into the specified property and can be used in the regular ANT instructions. 

Friday, April 13, 2012

ANT example on how to control whether to perform building or not depending on the version in your local perforce repository

Assume you have the ANT build procedure intended to be run on demand. Assume also you would like to automatically check if you have the latest version of your source files in your local version control repository (that is certainly not a problem if you use building as a part of continuous integration flow). This example shows how to design such the procedure using perforce version control. The same approach can be used for any command-line VC-system.

Say that will be enough to use the following properties to make your solution customizable:
changelist.to.sync.number - the number of changelist. Being not set means you'd like to sync the head revision
p4.hostp4.portp4.userp4.passwordp4.client - set of properties to establish the connection to perforce server
p4.project.path - path to project to sync on perforce server
email.serveremail.portemail.passwordemail.useremail.sender.aliasemail.sender.from, email.to - set of properties to establish connection with email server.

Here is the ANT script doing what we want to

<project name="deploy.on.demand" default="default">
 
 <!-- Load properties to customize particular run -->
 <property file="${basedir}/builder.properties"/>
 
 <target name="default">

  <!-- Build the suffix for p4 cmd line -->
  <condition property="changelist.to.sync" else="#head" value="@${changelist.to.sync.number}">
   <isset property="changelist.to.sync.number"/>
  </condition>
  
  <!-- Call perforce sync command -->
  <exec executable="p4" failonerror="true" outputproperty="exec.output">
   <arg value="-p${p4.host}:${p4.port}"/>
   <arg value="-u${p4.user}"/>
   <arg value="-P${p4.password}"/>
   <arg value="-c${p4.client}"/>
   <arg value="sync"/>
   <arg value="${p4.project.path}/...${changelist.to.sync}"/>
  </exec>
  
  <!-- Parce perforce output to figure out if you have the latest virsion -->
  <condition property="dont.do.further.actions">
   <contains string="${exec.output}" substring="file(s) up-to-date"/>
  </condition>

  <!-- Call actual build procedure if you don't have one-->
  <antcall target="futher.actions"/>
  
  <!-- Notify the user you are not going to build if you have one-->
  <antcall target="notify.reject.building"/>
  
 </target> 
 
 <target name="notify.reject.building" if="dont.do.further.actions">
  <mail mailhost="${email.server}" mailport="${email.port}" ssl="flase" password="${email.password}" user="${email.user}" subject="Building rejected!">
   <from name="${email.sender.alias}" address="${email.sender.from}"/>
   <to address="${email.to}"/>
   <message>Building is not going to be performed as you already have requested CL (${changelist.to.sync}) in your local repository</message> 
  </mail>
 </target>

 <target name="futher.actions" unless="dont.do.further.actions">

  <!-- Make actual build here -->
  
 </target>

</project>

First we should execute the command to sync the branch we'd like to build from. To do this we have to convert the desired version number to the syntax perforce understands. Exec target is configured to output the result to the specific property.
Then we parse that property to know if it contains the sub-string perforce returns when it is nothing new to sync. Once it has the corresponding build target is triggered. Otherwise the mail notification is sent so that anyone interested knows the build procedure is not going to be performed.


Wednesday, August 24, 2011

P4 Java API. How to work with temporary clients.

Check new series of the articles. Review and user experience on test management systems. Functionality and usability.
-------------------

There is not a lot of information about perforce Java API. However you may find the examples of how to apply the common use cases in your code. I tried to touch surrounding cases and faced the problem that had been resolved with the only decompilation help.

So assume you do not want to use the existing client and you do want to use the temporary one. How to address such the requirement. Here is the code from me.


package ar.p4apihelpers;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.List;
import java.util.UUID;

import com.perforce.p4java.client.IClient;
import com.perforce.p4java.core.IMapEntry.EntryType;
import com.perforce.p4java.core.file.FileSpecBuilder;
import com.perforce.p4java.core.file.IFileSpec;
import com.perforce.p4java.exception.P4JavaException;
import com.perforce.p4java.impl.generic.client.ClientView;
import com.perforce.p4java.impl.generic.client.ClientView.ClientViewMapping;
import com.perforce.p4java.impl.mapbased.client.Client;
import com.perforce.p4java.server.IServer;
import com.perforce.p4java.server.IServerInfo;
import com.perforce.p4java.server.ServerFactory;

public class P4APISyncUp{
 public static void main(String[] args) throws URISyntaxException, IOException, P4JavaException {
  // Generating the files to sync-up
  String[] pathsUnderDepot = new String[]{
    "//depot/path1/file1.java#head",
    "//depot/path1/file2.java#head",
    "//depot/path1/path2/file1.java#head"
  };
  // Instantiating the server
  IServer p4Server = ServerFactory.getServer("p4java://p4.fakeserver.com:1666", null);
  p4Server.connect();
  // Authorizing
  p4Server.setUserName("secretname");
  p4Server.login("secretpassword");
  // Just check you are connected successfully
  IServerInfo serverInfo = p4Server.getServerInfo();
  System.out.println(serverInfo.getServerLicense());
  // Creating new client
  IClient tempClient = new Client();
  // Setting up the name and the root folder
  tempClient.setName("tempClient" + UUID.randomUUID().toString().replace("-", ""));
  tempClient.setRoot("c:/temp");
  tempClient.setServer(p4Server);
  // Setting the client as the current one for the server
  p4Server.setCurrentClient(tempClient);
  // Creating Client View entry
  ClientViewMapping tempMappingEntry = new ClientViewMapping();
  // Setting up the mapping properties
  tempMappingEntry.setLeft("//depot/...");
  tempMappingEntry.setRight("//" + tempClient.getName() + "/...");
  tempMappingEntry.setType(EntryType.INCLUDE);
  // Creating Client view
  ClientView tempClientView = new ClientView();
  // Attaching client view entry to client view
  tempClientView.addEntry(tempMappingEntry);
  tempClient.setClientView(tempClientView);
  // Registering the new client on the server
  System.out.println(p4Server.createClient(tempClient));
  // Surrounding the underlying block with try as we want some action
  // (namely client removing) to be performed in any way 
  try{
   // Forming the FileSpec collection to be synced-up
   List<ifilespec> fileSpecsSet = FileSpecBuilder.makeFileSpecList(pathsUnderDepot);
   // Syncing up the client
   tempClient.sync(FileSpecBuilder.getValidFileSpecs(fileSpecsSet), true, false, false, false);
  }finally{
   // Removing the temporary client from the server
   System.out.println(p4Server.deleteClient(tempClient.getName(), false));
  }
 }
}


Please point the attention to the lines #42 and #50 of this code. Forgetting to call #42 leads to the following exception on execution #64:
Exception in thread "main" java.lang.NullPointerException
at com.perforce.p4java.impl.mapbased.client.Client.sync(Client.java:496)
at com.perforce.p4java.impl.mapbased.client.Client.sync(Client.java:477)
at ar.p4apihelpers.P4APISyncUp.main

If you forget to call #50 you will get the exception like this:
Exception in thread "main" com.perforce.p4java.exception.RequestException: Error in client specification.
Error detected at line 10.
Null directory (//) not allowed in 'null//depot/...'.

at com.perforce.p4java.impl.mapbased.server.Server.handleErrorStr(Server.java:3834)
at com.perforce.p4java.impl.mapbased.server.Server.createClient(Server.java:2099)
at ar.p4apihelpers.P4APISyncUp.main

Monday, February 21, 2011

Eclipse. Perfoce. Error in change specification. Can't include file(s) not already opened. Error in change specification. Can't include file(s) not already opened.

Recently I faced the problem using P4-Eclipse integration. The message said like the one specified in this post's subject. So, googling gave nothing but only the that lot of people face the same kind of problem but actually of different reasons. Now after the short investigation I can share my one.
It seems eclipse gets mad if the current client spec is set up with some client view different from looking straight into the storage root. You should either to remap client view or to use another client spec.