The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

VCS::CMSynergy - Perl interface to IBM Rational Synergy

SYNOPSIS

use VCS::CMSynergy;

$ccm = VCS::CMSynergy->new(%attr);

($rc, $out, $err) = $ccm->ccm($ccm_command, @ccm_args);
($rc, $out, $err) = $ccm->any_ccm_command(@ccm_args); 

$aref = $ccm->query(@ccm_args);
$aref = $ccm->query_arrayref($query, @keywords);
$aref = $ccm->query_hashref($query, @keywords);
$aref = $ccm->query_object($query, @keywords);

$aref = $ccm->finduse(@args);
$path = $ccm->findpath($file_spec, $proj_vers);

$aref = $ccm->history(@ccm_args);
$aref = $ccm->history_arrayref($file_spec, @keywords);
$aref = $ccm->history_hashref($file_spec, @keywords);

$aref = $ccm->ls(@ccm_args);
$aref = $ccm->ls_object($file_spec, @keywords);
$aref = $ccm->ls_arrayref($file_spec, @keywords);
$aref = $ccm->ls_hashref($file_spec, @keywords);

$value = $ccm->get_attribute($attr_name, $file_spec);
$ccm->set_attribute($attr_name, $file_spec, $value);
$hash_ref = $ccm->list_attributes($file_spec);

$delim = $ccm->delimiter;
$database = $ccm->database;
$ENV{CCM_ADDR} = $ccm->ccm_addr;

This synopsis only lists the major methods.

Methods that don't need a Synergy session are described in VCS::CMSynergy::Client. In fact, VCS::CMSynergy is derived from VCS::CMSynergy::Client.

Methods for administering users and their roles are described in VCS::CMSynergy::Users.

DESCRIPTION

use VCS::CMSynergy;

my $ccm = VCS::CMSynergy->new(database => "/ccmdb/test/tut62/db");

$ccm->checkout(qw(foo/bar.c@foo~user -to test))
  or die "checkout failed: ".$ccm->error;

my $csrcs = $ccm->query_hashref("type = 'csrc'",
                                qw(displayname modify_time));
if ($csrcs)
{
  print "$_->{displayname} $_->{modify_time}\n" foreach (@$csrcs);
}

OPTIONS

The following optional features can be enabled at compile time with the notation

use VCS::CMSynergy ':option';

:cached_attributes

This causes VCS::CMSynergy::Objects to keep a cache of attribute names and values. The cache is only maintained for those attributes that are actually accessed by the program. See "ATTRIBUTE METHODS" in VCS::CMSynergy::Object for a list of methods perusing this cache.

Note that this cache is only maintained if you use VCS::CMSynergy::Object methods (including the "TIEHASH INTERFACE" in VCS::CMSynergy::Object) and will get inconsistent if you mix VCS::CMSynergy::Object and VCS::CMSynergy calls on the same object.

:tied_objects

If this option is in effect. you can use a VCS::CMSynergy::Object in the same way you would use a hash reference. The available keys are the underlying Synergy object's attributes. See "TIEHASH INTERFACE" in VCS::CMSynergy::Object for details.

GENERAL METHODS

new

my $ccm = VCS::CMSynergy->new( database => "/ccmdb/foo/db" )
            or die VCS::CMSynergy->error;

Starts a new Synergy session. Returns a session handle if it succeeds.

If it fails to start a session, it returns undef. Use VCS::CMSynergy->error to get the error string printed by Synergy.

Multiple simultaneous sessions to multiple databases or with engines running on different hosts, even using different versions of Synergy, are supported.

new issues a ccm start command and remembers the CCM_ADDR in the session object (together with other session state). The session is stopped (ccm stop) when the session object is destroyed (see "DESTROY").

new is called with an attribute hash. The following attributes are currently supported:

database (string)

Synergy database path.

This is the only attribute required on Unix systems.

host (string)

Synergy engine host to use.

It defaults to the local host.

role (string)

User's initial Synergy role.

It defaults to developer.

user (string)

Synergy user.

This attribute is available and required on Windows systems only.

password (string)

User's password.

This attribute is required on Windows systems or when using ESD to connect to the Synergy engine.

server (string)

Synergy server URL (for Synergy 7.0 and above). Specifying this instead of host will start your Synergy session in web mode.

Note that support for web mode is incomplete and only lightly tested.

ini_file (string) (classic mode only)

Synergy ini file to use.

In contrast to the Synergy ccm start command there is no default ini file consulted. (On Unix systems this is achieved by executing ccm start with the option -f /dev/null.) The reason is that we want scripts to behave in a reproducible way. Otherwise the script might accidentally work with the current contents of the current user's ini file, but might fail when invoked by another user. Or it might fail when invoked by the same user at a later time because of changes to her ini file (e.g. because of another session between invocations of the script). So if you really want to rely on an ini file, you have to supply it explicitly.

This option is only valid when using the classic mode to connect to Synergy.

CCM_ADDR (string)

Specifies the RFC address of an established Synergy session.

If you specify this attribut "new" does not create a new session, but will attach to the one specified. Also, implicitly sets KeepSession to true so that destruction of the new session handle will not cause a ccm stop. However, setting KeepSession explicitly will take precedence.

Note that there is no default value. In particular, "new" ignores the environment variable of the same name.

CCM_HOME (string)

Value of the CCM_HOME environment variable to use for this session.

It defaults from the environment variable of the same name, i.e. $ENV{CCM_HOME}.

This is only of interest if you have multiple version of Synergy installed. You can have simultaneous sessions using different Synergy versions (the module takes care of setting the CCM_HOME variable appropriately before issuing any ccm commands).

ui_database_dir (string)

Specifies the path name to which your database information is copied when you are running a remote client session. This corresponds to the -u pathname option for ccm start.

Note: This option is particularly useful for Windows clients. If "new" fails with something like

Server Database Path ... is not accessible from this Client.   
Please specify a Client Database Path

you should specify this option with a local directory path, e.g.

my $ccm = VCS::CMSynergy->new(..., ui_database_dir => 'c:\\temp', ...);

The value is what you would enter under "Client Information"/"Database Path" in the GUI's "Startup Info" window. Or you can set ui_database_dir in the [Options] section of the system ini file (note that setting it in your personal ini file won't do, as this file is not read by "new" by default).

remote_client (boolean)

If the value is true, it specifies that you want to start the Synergy session as a remote client. This corresponds to the -rc option for ccm start. This option is only useful on Unix systems. It defaults to false.

PrintError (boolean)

This attribute can be used to force errors to generate warnings (using carp) in addition to returning error codes in the normal way. When set to true, any method which results in an error occurring will cause the corresponding $ccm->error to be printed to stderr.

It defaults to true.

Note: "PrintError" and "RaiseError" below are stolen from the excellent DBI module.

RaiseError (boolean)

This attribute can be used to force errors to raise exceptions (using croak) rather than simply return error codes in the normal way. When set to true, any method which results in an error will cause effectively a die with the actual $ccm->error as the message.

It defaults to false.

If you turn RaiseError on then you'd normally turn PrintError off. If PrintError is also on, then the PrintError is done first (naturally).

Typically RaiseError is used in conjunction with eval { ... } to catch the exception that's been thrown and followed by an if ($@) { ... } block to handle the caught exception.

If you want to temporarily turn RaiseError off (inside a library function that is likely to fail, for example), the recommended way is like this:

{
  local $ccm->{RaiseError};  # localize and turn off for this block
  ...
}

The original value will automatically and reliably be restored by Perl, regardless of how the block is exited. The same logic applies to other attributes, including PrintError.

HandleError (code ref)

This attribute can be used to provide your own alternative behavior in case of errors. If set to a reference to a subroutine then that subroutine is called when an error is detected (at the same point that "RaiseError" and "PrintError" are handled).

The subroutine is called with three parameters: the error message string that "RaiseError" and "PrintError" would use, the VCS::CMSynergy object being used, and the value being returned by the method that failed (typically undef).

If the subroutine returns a false value then the "RaiseError" and/or "PrintError" attributes are checked and acted upon as normal. Otherwise the error is considered "handled" and execution proceeds normally with a return from the method.

For example, to "die" with a full stack trace for any error:

use Carp;
$ccm->{HandleError} = sub { confess(shift) };
KeepSession (boolean)

If this attribute is true then destruction of the new session handle will not cause a ccm stop.

This may be used if you want to create a new Synergy session in one program and then re-use it in another program (since session creation is a rather time consuming operation). In this case you should use /ccm_addr to extract the session's RFC address (after /new returns) and somehow pass it on to the other program.

It defaults to false unless you also specify CCM_ADDR.

utf8 (boolean)

If this attribute is true then all strings retrieved from Synergy will be Perl Unicode strings (instead of byte strings). It defaults to false.

The only exception is the string returned from the cat_object method which is always a byte string.

Be sure to study the perlunicode documentation when setting this attribute to true.

Note that this will only work if the Synergy session is running in a UTF-8 locale (at least on Linux), e.g. by setting the environment variable LANG to an appropriate (system dependent) value. The variable must be set before starting a Synergy session. Setting it after starting the session has no effect on the ouput of the ccm program.

UseCoprocess (boolean) (classic mode only)

This feature is highly experimental, use it at your own risk.

You must have the Expect module installed to use this feature. (Since Expect is not available for Win32 systems, UseCoprocess is ignored there.)

If UseCoprocess is false, VCS::CMSynergy.pm executes a separate ccm process whenever it invokes the Synergy CLI, e.g.

$ccm->checkout('foo.c');
$ccm->set_attribute('color', 'foo.c', 'blue');
$csources = $ccm->query("name match '*.c'");

results in the execution of the following three processes:

ccm checkout foo.c
ccm attribute -modify color -value blue foo.c
ccm query "name match '*.c'"

In particular, we incur the startup overhead of ccm three times. This overhead is noticable, esp. if you are doing lots of Synergy operations.

If UseCoprocess is true, only one ccm process per Synergy session ever gets executed. The way it works is that VCS::CMSynergy->new starts an "interactive" (i.e. one invoked without arguments) ccm process in the background. Later invocations of the Synergy CLI pipe their commands to its input and read back the output (up to the next "ccm>" prompt). The actual command is then followed in the same way by set error to retrieve the success status. Destruction of the session object will cause termination of this "coprocess" (via "stop" or "exit" depending on the setting of "KeepSession").

The "coprocess" method avoids the startup overhead, but may run into other problems:

  • The "interactive" ccm imposes stricter limits on the length of one CLI command (experimentally put at ~2000 bytes) than the "batch" ccm (where the limit on the arguments of a process is typically imposed by the operating system). Moreover, it will silently truncate the command and not signal an error (unless the truncation causes a syntax error).

  • The current method to communicate with the "coprocess" does not allow for separation of its stdout and stderr.

  • UseCoprocess does not work under Win32 at all.

The default value of UseCoprocess is false.

DESTROY

$ccm->DESTROY;

Stops the Synergy session represented by the session handle by executing ccm stop (unless the session has the KeepSession attribut set).

You should never call this method explicitly, as it is invoked by the Perl runtime when the Perl process exits (either by calling exit or because of a die). Hence, a script using the VCS::CMSynergy module will not leave any Synergy sessions hanging around.

Actually, the Perl runtime will call DESTROY when the last reference to a session handle goes out of scope, so in the following example each session will be stopped as soon as one loop through the foreach body is completed, i.e. there is at most one session in progress at any one time:

my @databases = ...;          # a list of Synergy databases
foreach my $db (@databases)
{
  my $ccm = VCS::CMSynergy->new( database => $db, ... );
  ...
  # perform some operation on $db
  ...
  # session is stopped as "my" variable $ccm is about to go out of scope
}

Note: The correct way to explicitly stop a session is neither

$ccm->stop;

nor is it

$ccm->DESTROY;

Though both forms will execute ccm stop, the first form makes $ccm a VCS::CMSynergy object with an invalid RFC address (i.e. attribute CCM_ADDR), while the second form leaves you with an "empty" VCS::CMSynergy object. Instead, you should rather say

$ccm = undef;

ccm

($rc, $out, $err) = $ccm->ccm($command, @args);
$ok = $ccm->ccm($command, @args);

This is the workhorse of the VCS::CMSynergy module. It executes ccm with command $command and (optional) parameters @args.

In array context (the first form above) ccm returns a three-element array consisting of the (operating system) exit code of ccm, and what ccm printed on stdout and stderr. Note that the exit code is 0 if ccm operated successfully. On DOSish operating systems the (possibly multi-line) strings $out and $err have been read by Perl in "text" mode, i.e. contain LF characters instead of CRLF. In any case, $out and $err have been chomped.

In scalar context (the second form above) ccm returns the "logical" exit code, i.e. $rc == 0, so that you can write:

$ccm->ccm('checkout', $file_spec) 
    or die "checkout failed: ".$ccm->error;

If you do check the return value (i.e. ccm is called in non-void context) then no automatic error handling (cf. "RaiseError", "PrintError" and "HandleError") is done.

Note that you must pass every ccm argument or option as a single Perl argument. For literal arguments the qw() notation may come in handy, e.g.

($rc, $out, $err) = $ccm->ccm(qw(finduse -state working));

Most specialized methods in the VCS::CMSynergy module are ultimately implemented via the ccm method. Using it directly is only recommended for commands that perform some action, e.g. ccm checkout, as opposed to query-like commands. For the latter, e.g. ccm query, use one of the methods that return the information in structured form, e.g. query_arrayref or query_hashref, instead of having to parse $out yourself.

In fact, there is a shortcut for "action" commands: if you call a non-existent method on a VCS::CMSynergy object, it tries to invoke the ccm method with the original method name as the $command followed by the parameters of the original call, i.e.

$ccm->checkout($file_spec);

and

$ccm->ccm('checkout', $file_spec);

are equivalent (given that there is no real checkout method). Return values are those of ccm (depending on context). This is accomplished by a suitable AUTOLOAD method.

QUERY METHODS

query

$aref = $ccm->query(@args);

Executes the ccm query command with the given @args as parameters. The output (as formatted by the -format option) is split into lines. These are chomped and a reference to the resulting array of strings is returned.

If there a no hits, a reference to an empty array is returned. (Note that ccm query considers this an error, but VCS::CMSynergy does not.)

If there was an error, undef is returned.

Note that you must pass every ccm query argument or option as a single Perl argument. For literal arguments the qw() notation may come in handy. Example:

$result = $ccm->query(qw(-t csrc -f), '%displayname %modify_time');
print "$_\n" foreach (@$result);

If you are interested in the value of several attributes for the result set of the query, you should look at the query_arrayref and query_hashref methods that return this information in structured form. If you are only interested in the identity of objects in the result set, you should look at the query_object method.

Note that "query" will probably produce unpredictable results when the -format option references attributes that can have multi-line values, e.g. status_log. query_arrayref and query_hashref handle this case correctly.

query_arrayref, query_hashref

$aref = $ccm->query_arrayref($query, @keywords);
print "@$_\n" foreach @$aref;

$aref = $ccm->query_hashref($query, @keywords);
print "@$_{@keywords}\n" foreach @$aref;

query_arrayref and query_hashref execute ccm query with the query expression $query asking for the values of the built-in keywords or attributes supplied in @keywords. They both return a reference to an array of references, one per result row.

query_arrayref represents a row as an array containing the values of the keywords for that particular object in the result set (in the order given by @keywords).

query_hashref represents a row as a hash containing attribute and value pairs where the keys are the @keywords.

If the query returned no hits, both query_arrayref and query_hashref return a reference to an empty array.

If there was an error, undef is returned.

If the value of a keyword or an attribute is undefined or the attribute is not present, the actual value of the corresponding array or hash element is undef (whereas ccm query would print it as the string "<void>").

The following names may also be used as keywords though they are neither built-in nor attributes:

object

The value is a VCS::CMSynergy::Object representing the object in the result set.

finduse (classic mode only)

The value is a reference to a hash identifying in what parts of what projects the object is used. A key in the hash is the project's objectname. The hash value is the corresponding relative path (including the object's name) in the project. This information is the same as reported by ccm finduse. In fact, if this keyword is given, query_arrayref and query_hashref invoke ccm finduse -query $query rather than ccm query $query. Example:

my $result = $ccm->query_arrayref(
  "name = 'main.c'", qw(objectname finduse));

returns (as formatted by Data::Dumper):

$result = [
  [
    'main.c-1:csrc:3',        # objectname
    {                         # finduse
       'guilib-1.0'   => 'guilib/sources/main.c',
       'guilib-int'   => 'guilib/sources/main.c',
       'guilib-darcy' => 'guilib/sources/main.c'
    }
  ],
  ...
];

This keyword is only valid when using the classic mode to connect to Synergy.

objectname

objectname actually is a built-in keyword. However, Synergy ccm query -f %objectname returns the deprecated fullname (i.e. subsystem/cvtype/name/version) for certain model objects (e.g. try ccm query -f %objectname -i base) (but refuses to accept them as arguments later). Therefore VCS::CMSynergy will rewrite these fullnames to correct objectnames before returning them from query_arrayref or query_hashref.

task_objects

The value is a reference to an array of VCS::CMSynergy::Object representing the tasks associated with the object. The value is undef if there are no associated tasks. This keyword is implemented using the Synergy built-in keyword "%task".

cr_objects

The value is a reference to an array of VCS::CMSynergy::Object representing the change request associated with the object. The value is undef if there are no associated change requests. This keyword is implemented using the Synergy built-in keyword "%change_request".

baseline_project

The value is a VCS::CMSynergy::Project representing the object's baseline project. The value is undef if no baseline project exists. This keyword is implemented using the Synergy built-in keyword "%baseline".

baseline_object

The value is a VCS::CMSynergy::Object representing the object's baseline. The value is undef if the object isn't in a baseline. This keyword is implemented using the Synergy built-in keyword "%in_baseline".

Note the following differences from ccm query:

  • The keyword or attribute names given in @keywords should not contain a leading %. Example:

    my $result = $ccm->query_hashref("name match '*.c'", 
                                      qw(displayname type modify_time);
    foreach my $row (@$result)
    {
      print "$row->{displayname} last modified at $row->{modify_time}\n";
      ...
    }
  • These methods do not support any of the shortcut query options of the ccm query command, e.g. -o owner or -n name. However, a different shortcut syntax is supported, see "shortcut query notation".

  • $query may contain newlines to improve the legibility of longish queries with whitespace and line breaks. Any whitespace in $query will be replaced by a single blank before submitting it to ccm query.

query_object

$aref = $ccm->query_object($query, @keywords);

Executes ccm query with the query expression $query and returns a reference to an array of VCS::CMSynergy::Objects that satisfy the query.

If there a no hits, a reference to an empty array is returned.

If there was an error, undef is returned.

Note: This is a convenience method. It might be implemented using query_arrayref:

sub query_object
{
  my ($self, $query) = @_;
  my $ary = $self->query_arrayref($query, 'object') or return;
  [ map { $_->[0] } @$ary ];  # project onto first (and only) column
}

Calling query_object with an optional list of @keywords is only useful when ":cached_attributes" is in effect. It returns the same result as query_object, but the returned VCS::CMSynergy::Objects have their attribute caches primed for the attributes listed in @keywords. You could also view it as a fancy form of query_hashref where we don't store the attributes values of @keywords in some anonymous hash, but rather in the corresponding object. Thus the loop

for my $obj (@{ $ccm->query_object("...", qw(foo)) })
{
  print "$obj: foo=", $obj->get_attribute("foo"), "\n";
}

issues a total of one ccm calls. Note: this example assumes

use VCS::CMSynergy qw(:cached_attributes);

Note: query_object_with_attributes is a deprecated alias for query_object.

query_count

$n = $ccm->query_count($query);

Returns the number of objects matched by $query, 0 if nothing matched. This is the same as

scalar @{ $ccm->query_object($query) }

but it's implemented more efficiently (and also less prone to exhaust the 10 MB query result buffer in the Synergy engine).

If there was an error, undef is returned.

shortcut query notation

query_arrayref, query_hashref, query_object, query_object and "query_count" support a shortcut notation for their common $query parameter. To use this shortcut, supply an array reference for $query (instead of a simple string):

$result = $ccm->query_hashref(
  [ type => 'csrc', name => '*.cpp' ], qw(objectname status));

The array must have an even number of elements, shown here as a list of $key => $value pairs. Each pair represents a simple query. Simple queries with the same $key are combined with "or" All other simple queries are combined with "and".

Typically $key => $value is translated to Synergy query syntax as $key = '$value'. Note the quotes around $value. However, quotes are omitted if $value is either the string "TRUE" or "FALSE".

If $value contains a wildcard character (* or ?) the pair is translated as $key match '$value'.

If $value is an array reference the translation is the alternative of the array elements, e.g.

stooge => [qw( larry curly moe )]

is translated as

"(stooge='larry' or stooge='curly' or stooge='moe')"

query convenience functions: ANY_OF, NONE_OF

use VCS::CMSynergy qw( ANY_OF NONE_OF );

$aref = $ccm->query_object(ANY_OF(status => qw( integrate released )), ...);

ANY_OF(key => @values and NONE_OF(key => @values) are convenience functions returning strings that can be used to build queries. For example, the above is the same as

$aref = $ccm->query_object("(status='integrate' or status='released')", ...);

The result of ANY_OF and NONE_OF can safely be used in a query where syntactically an expression is expected.

Note that you must explicity import ANY_OF and NONE_OF too use them without the full module name.

history

$aref = $ccm->history(@args);

Executes the ccm history command with the given @args as parameters. The output (probably formatted by the -format option) is split into chunks at the divider line (a line consisting of lots of asterisks). A reference to the resulting array of (multi-line) strings is returned.

If there was an error, undef is returned.

Note that you must pass every ccm history argument or option as a single Perl argument. For literal arguments the qw() notation may come in handy.

If you are interested in the successor or predecessor or certain attributes of an object in the history, you should look at the history_arrayref and history_hashref methods that return this information in structured form.

history_arrayref, history_hashref

$aref = $ccm->history_arrayref($file_spec, @keywords);
$aref = $ccm->history_hashref($file_spec, @keywords);

history_arrayref and history_hashref execute ccm history for $file_spec asking for the values of the built-in keywords or attributes supplied in @keywords. The both return a reference to an array of references, one per history entry.

history_arrayref represents a history entry as an array containing the values of the keywords for that particular object in the history (in the order given by @keywords).

history_hashref represents a history entry as a hash containing attribute and value pairs where the keys are the @keywords.

If there was an error, undef is returned.

If the value of a keyword or an attribute is undefined or the attribute is not present, the actual value of the corresponding array or hash element is undef (whereas ccm history would print it as the string "<void>").

The following names may also be used as keywords though they are neither built-in nor attributes:

predecessors

The value returned is a reference to an array of VCS::CMSynergy::Objects that represent the given object's predecessors.

successors

The value returned is a reference to an array of VCS::CMSynergy::Objects that represent the given object's successors.

object, objectname, task_objects

For these pseudo keywords see the description of the query_arrayref and query_hashref methods.

Note the following differences from ccm history:

  • Only one $file_spec is allowed.

  • There is no -p (project) option. If you want to get the history of a project use the full objectname of the project for $file_spec.

  • The keyword or attribute names given in @keywords should not contain a leading %. Example:

    my $result = $ccm->history_hashref(
      'math.h-1:incl:1', qw(displayname modify_time successors));
    
    foreach my $row (@$result)
    {
      print "$row->{displayname}: last modified at $row->{modify_time}\n";
      print "\t$_\n" foreach (@{ $row->{successors} });
      ...
    }

finduse

$aref = $ccm->finduse(@args);

Executes the ccm finduse command with the given @args as parameters. It returns a reference to an array of rows, one per file_spec given in @args, or one per query result if -query $query_expression is present in @args.

Each row is a reference to an array of two elements. The first element is the description of the object. The second element is a reference to a hash identifying in what parts of what projects the object is used. A key in this hash is the project's objectname and the value is the corresponding relative path in the project. If there are no uses of the object in the given scope the hash is empty. This usage information is in the same form as that for the pseudo keyword finduse of the query_arrayref and query_hashref methods.

If there was an error, undef is returned. If an element of @args refers to a non-existent object, the corresponding element in the return value is undef.

Note that you must pass every ccm finduse argument or option as a single Perl argument. For literal arguments the qw() notation may come in handy.

If you are interested in usage information for all objects matching a query you should look at the query_arrayref and query_hashref methods, esp. the finduse keyword.

Example (recreate the output of the ccm finduse command):

foreach (@{ $ccm->finduse(@args) })
{
  my ($desc, $uses) = @$_;
  print "$desc\n";
  if (keys %$uses)
  {
      while (my ($proj_vers, $path) = each %$uses)
      {
        print "\t$path\@$proj_vers\n"
      }
  }
  else
  {
      print "\tObject is not used in scope.\n";
  }
}

findpath

$path = $ccm->findpath($file_spec, $proj_vers);

This is a convenience function. It returns the relative pathname (including the objects's name) for the object $file_spec within the project $proj_vers.

Returns undef if $file_spec is not used in $proj_vers or if $file_spec does not exist.

Example:

$ccm->findpath("main.c-1:csrc:3", "guilib-darcy"); 

returns

"guilib/sources/main.c"

relations_arrayref

$ccm->relations_arrayref(%options);

my $related = $ccm->relations_arrayref(to => "bufcolor.c-2:csrc:1");

Executes ccm relate -show ... where %options may contain any of the following keys:

from => $file_spec, to => $file_spec

Restricts one or both ends of the relation. The option value may be any $file_spec accepted by Synergy including a VCS::CMSynergy::Object.

name => $string

Restricts the return value to relations of type $string, e.g. associated_cv or successor.

The result is a reference to an array of 4-tuples

[ $from, $name, $to, $create_time ]
$from, $to

The value is the objectname of the "from" or "to", resp., element of the relation.

$name

The value is name of the relation.

$create_time

The value is the time the relation was created.

If there are no hits, a reference to an empty array is returned.

If there was an error, undef is returned.

relations_hashref

$ccm->relations_hashref(%options);

my $related = $ccm->relations_hashref(
  to              => "bufcolor.c-2:csrc:1",
  from_attributes => [ qw/objecctname status owner/ ]);

Executes ccm relate -show ... where %options may contain any of the following keys:

from => $file_spec, to => $file_spec

Restricts one or both ends of the relation. The option value may be any $file_spec accepted by Synergy including a VCS::CMSynergy::Object.

name => $string

Restricts the return value to relations of type $string, e.g. associated_cv or successor.

from_attributes => \@keywords, to_attributes => \@keywords

The option value is a reference to an array of attributes that should be retrieved for the "from" and "to" objects of a relation, resp.

The result is a reference to an array of hashes where each hash has exactly four keys describing a relation between two objects:

from

The value describes the "from" object of the relation.

If from_attributes was not specified the value is the objectname of the "from" object.

If from_attributes was specified the value is a reference to a hash of attribute names and values, its keys given by from_attributes.

The pseudo keywords object and task_objects (see "query_arrayref, query_hashref") may be used in from_attributes.

to

The value describes the "to" object of the relation.

The value depends on to_attributes as described for the "from" key.

name

The value is name of the relation.

create_time

The value is the time the relation was created.

If there are no hits, a reference to an empty array is returned.

If there was an error, undef is returned.

relations_object

my $related = $ccm->relations_object(%options);

Executes ccm relate -show ... where %options are the same as described for "relations_hashref".

The result is a reference to an array of hashes where each hash has exactly four keys describing a relation between two objects:

from, to

The value is a VCS::CMSynergy::Object.

name

The value is name of the relation.

create_time

The value is the time the relation was created.

If there a no hits, a reference to an empty array is returned.

If there was an error, undef is returned.

If from_attributes or to_attributes are specified, these are used as hints to prime the attribute caches of the "from" or "to" VCS::CMSynergy::Objects, resp., similar to the optional @keywords argument for "query_object". This is only useful when ":cached_attributes" is in effect.

project_tree

$hash = $ccm->project_tree(\%options, $project);
$hash = $ccm->project_tree(\%options, $project1, $project2 ...);

project_tree traverses the given project(s) and constructs a mapping of path names to project members. It doesn't need a workarea. $project may be any project specification (in project-version form, an objectname or a VCS::CMSynergy::Object). project_tree returns a reference to a hash where the keys are the (relative workarea) path names of project members.

If given one project (the first form above), hash values are $project's members, given as VCS::CMSynergy::Objects.

If given two or more projects (the second form), a hash value is an array of VCS::CMSynergy::Objects where the first element is the member of $project1 mapped to the path key, the second element is the member of $project2 etc. If there is no member mapped to a path in a particular project, the corresponding element in the array is undef. This form of project_tree may be useful for comparing projects, see below for an example.

If there was an error, project_tree returns undef.

The first argument, \%options, is either undef or a hash reference of options for project traversal:

subprojects (boolean)

If this option is true, the mapping will recurse into subprojects. It is false by default. It corresponds to the option of the same name for "traverse" in VCS::CMSynergy::Project. If this option is false and mark_projects is also false, no indication of sub projects is present in the tree; if mark_projects is true, then the tree will contain just the sub project VCS::CMSynergy::Objects, but nothing "below" them.

attributes (array ref)

This option is only useful if ":cached_attributes" is in effect. All returned VCS::CMSynergy::Objects will have their attribute caches primed for the given attributes. See the description of the option of the same name for "traverse" in VCS::CMSynergy::Project.

pathsep (string)

Use pathsep as the separator for the path names (the keys of the returned hash). If you do not specify this, project_tree uses the path separator appropriate for the operating system the script is running on.

mark_projects (boolean)

It decides what will be shown as the value for a path corresponding to a (sub) project. If false, the value refers to the (sub) project's top level directory (a VCS::CMSynergy::Object of cvtype "dir"). If true, the value refers to the (sub) project itself (a VCS::CMSynergy::Object with cvtype "project").

This option is false by default.

Note that if subprojects is false, this option also decides whether a path corresponding to a sub project is present in the tree at all.

omit_top_dir (boolean)

If true, strip the first component (which is the name of $project and also its top level diretory) from all paths. Also omit the path corresponding to $project's top level directory) altogether.

This option is false by default.

The following example shows how to compute the "difference" between two projects expressed in the file system.

my $tree = $ccm->project_tree(undef, $proj1, $proj2);
foreach my $path (sort keys %$tree)
{
  my ($obj1, $obj2) = @{ $tree->{$path} };

  print("added $path: $obj2\n"), next      unless defined $obj1;
  print("deleted $path: $obj1\n"), next    unless defined $obj2;
  print("changed $path: $obj1 -> $obj2\n") unless $obj1 eq $obj2;
}

project_diff

$ccm->project_diff(\%options, $old_project, $new_project, $differ);

project_diff first calls "project_tree" on $old_project, $new_project and then drives $differ using the resulting map. This is a convenience method for computing the "difference" between two projects in various forms. See below for a simple example and ccm_project_diff for a complete working program.

$differ must be an object supporting the following methods:

added($path, $new)

This is called for every VCS::CMSynergy::Object only in $new_project; arguments are the path and the added object.

deleted($path, $old)

This is called for every VCS::CMSynergy::Object only in $old_project; arguments are the path and the deleted object.

changed($path, $old, $new)

This is called when the VCS::CMSynergy::Objects in $old_project and $new_project at $path are different; arguments are the path and both objects.

The above methods are mandatory. The following methods are called when $differ supports them:

identical($path, $obj)

This is called when the VCS::CMSynergy::Objects in $old_project and $new_project at $path are identical; arguments are the path and the object.

start($old_project, $new_project)

This is called before the traversal of the project tree starts; arguments are the original VCS::CMSynergy::Projects supplied to project_diff.

finish()

This is called after traversal of the project tree is completed; no arguments are supplied; project_diff will return whatever this method returns (if $differ doesn't support this method, project_diff returns undef).

Methods with a $path argument are called in ascending path sort order, esp. path children are processeed after their parents.

The first parameter, \%options, is the same as the corresponding parameter for "project_tree" and is passed through unchanged (except for option pathsep which is currently hardwired to "/"). Additionally the following option is recognized:

hide_sub_trees (boolean)

This option controls the handling of whole added or deleted sub trees. If it is true, $differ's added or deleted methods will only be called for the root of an added or deleted sub tree, all other calls are suppressed. Otherwise, added or deleted will be called for any path in the sub tree.

It defaults to false.

The following example prints a simple list of changes between two projects. All objects are listed with their path in filesystem first; "deleted" objects are prefixed with "-", "added" objects are prefixed with "+", different objects at the same path are prefixed with "!", and ouput for identical objects is suppressed.

{
  package SimpleDiffer;
  sub new     { my ($class) = @_; bless {}, $class; }
  sub added   { my ($self, $path, $new) = @_; 
                print "+ $path $new\n"; }
  sub deleted { my ($self, $path, $old) = @_; 
                print "- $path $old\n"; }
  sub changed { my ($self, $path, $old, $new) = @_; 
                print "! $path $old $new\n"; }
}
...
$ccm->project_diff({ hide_sub_trees => 1 }, 
                   $old_project, $new_project, SimpleDiffer->new);

ATTRIBUTE METHODS

get_attribute

$value = $ccm->get_attribute($attr_name, $file_spec);

Get the value of the attribute $attr_name for $file_spec (using ccm attribute -show).

If the attribute isn't defined for $file_spec, undef is returned.

If RaiseError is not set and an error occurs (e.g. object $file_spec doesn't exist), undef will be returned.

Note the following differences from ccm attribute -show:

  • Only one $file_spec is allowed.

  • There is no -p (project) option. If you want to get an attribute of a project use the full objectname of the project for $file_spec.

  • It is not an error to get the value of an attribute that isn't defined for the particular object. Instead, check the return value of "get_attribute" with defined as an attribute's value can never be undef.

set_attribute

$ccm->set_attribute($attr_name, $file_spec, $value);

Set the value of the attribute $attr_name for $file_spec to $value (usually using ccm attribute -modify, but see below).

Returns $value on success. If RaiseError is not set and an error occurs (e.g. attribute $attr_name does not exist on object $file_spec), undef will be returned.

This works for all types of attributes, even those of type text (or derived from text) and with $values that consist of multiple lines, have arbitrary length or are empty strings.

Note the following differences from ccm attribute -modify:

  • If the attribute $attr_nameis inherited, ccm attribute -modify $attr_name will fail. But set_attribute will retry with ccm attribute -create $attr_name -force in this case (thereby converting the attribute to a local attribute). I.e. whenever "get_attribute" indicates that an attribute exists (by returning something defined), you can always set its value with set_attribute (given you have necessary permissions).

  • Only one $file_spec is allowed.

  • There is no -p (project) option. If you want to set an attribute of a project use the full objectname of the project for $file_spec.

create_attribute

$ccm->create_attribute($attr_name, $type, $value, @file_specs);

Create attribute $attr_name of type $type on all objects given by @file_specs (using ccm attribute -create). You must specify an initial value (something other than undef) as $value.

Returns true on success and undef on failure.

Note the following differences from ccm attribute -create:

  • The initial value is mandatory.

  • There is no -p (project) option. If you want to set an attribute of a project use the full objectname of the project for $file_spec.

delete_attribute

$ccm->delete_attribute($attr_name, @file_specs);

Delete attribute $attr_name from all objects given by @file_specs (using ccm attribute -delete).

Returns true on success and undef on failure.

Note the following differences from ccm attribute -create:

  • There is no -p (project) option. If you want to set an attribute of a project use the full objectname of the project for $file_spec.

copy_attribute

$ccm->copy_attribute($attr_name, $from_file_spec, @to_file_specs);
$ccm->copy_attribute($attr_name, $flags, $from_file_spec, @to_file_specs);

Copy attribute $attr_name from $from_file_spec by objects given by @to_file_specs (using ccm attribute -copy).

Returns true on success and undef on failure.

You can specify multiple attributes to copy by passing a reference to an array of attribute names as $attr_name.

The optional $flags must be reference to an array containing a subset of the following strings: "append", "subproj", "suball", e.g.

$ccm->copy_attribute($attr_name, [ qw(subproj suball) ], 
                     "proja-1.0:project:1", "projb-1.0:project:1");

Cf. the Synergy documentation on the attribute command for the meaning of these flags.

Note the following differences from ccm attribute -copy:

  • There is no -p (project) option. If you want to set an attribute of a project use the full objectname of the project for $file_spec.

list_attributes

$hash_ref = $ccm->list_attributes($file_spec);

Lists all attributes for $file_spec (using ccm attribute -la).

Returns a reference to a hash containing pairs of attribute name and attribute type (e.g. string, time). Returns undef in case of error.

Note the following differences from ccm attribute -la:

  • Only one $file_spec is allowed.

properties_hashref, properties_object

$aref = $ccm->properties_hashref(\@files_specs, @keywords);
for (my $i; $i < @files_specs; $i++) {
  print "$files_specs[$i]\n";
  print "  $_ => $aref->[$i]{$_}\n" foreach @keywords;
}

$aref = $ccm->query_object(@file_specs, @keywords);

properties_hashref and properties_object execute ccm property with the list of @files_specs asking for the values of the built-in keywords or attributes supplied in @keywords. Note that @files_specs is passed in as an array reference. They both return a reference to an array of references, one per item in @files_specs (and in the same order).

For properties_hashref an item is a hash containing attribute and value pairs where the keys are the @keywords. This is similar to the result of query_hashref.

For properties_object an item is the VCS::CMSynergy::Object corresponding to the item in @files_specs at the same index. properties_object is most useful when ":cached_attributes" is in effect. In this case, the returned VCS::CMSynergy::Objects have their attribute caches primed for the attributes listed in @keywords. You could also view it as a fancy form of properties_hashref where we don't store the attributes values of @keywords in some anonymous hash, but rather in the corresponding object. This is similar to the behavior of query_object.

For consistency, both property_hashref and properties_object return a reference to an empty array when invoked with an empty list of @files_specs.

If there was an error, undef is returned.

If the value of a keyword or an attribute is undefined or the attribute is not present, the actual value of the corresponding hash element is undef.

For the pseudo keywords available in @keywords see the description of the query_arrayref and query_hashref methods.

property

$value = $ccm->property($keyword, $file_spec);
$hash = $ccm->property(\@keywords, $file_spec);

The first form returns the value of property $keyword for $file_spec (using ccm properties -f ...). The second form returns the values of all properties in @keywords as a hash reference.

You can use any of the Synergy built-in keywords for $keyword or @keywords. If the value of a keyword is undefined, undef is returned (whereas ccm properties would print it as the string "<void>").

MISCELLANEOUS METHODS

cat_object

$contents = $ccm->cat_object($object);
$ccm->cat_object($object, $destination);

Retrieves the contents (the "source" in Synergy terminology) of an object without the need for a workarea using ccm cat.

For both forms above, $object must be a VCS::CMSynergy::Object. The first form returns the object's contents as a string (and undef on error). The second form "writes" the object's contents to $destination which can be any of the following:

scalar

the contents will be written into a file named $destination

SCALAR reference

the contents will be written into the buffer (string) $$destination

file handle or GLOB reference

the contents will be written (printed) onto the file handle

Note the following difference from ccm cat:

  • cat_object only accepts a single VCS::CMSynergy::Object as argument

types

@types = $ccm->types;

Returns an array of types using ccm show -types.

migrate_auto_rules

@mars = $ccm->migrate_auto_rules;

Uses ccm show -migrate_auto_rules to return an array of arrays (of three elements each), e.g.

@mars = (
  [ 'MAP_FILE_TO_TYPE',   '.*\\.xml$',       'xml'             ],
  [ 'MAP_FILE_TO_TYPE',   '.*\\.o$',         'relocatable_obj' ],
  [ 'MAP_TYPE_TO_IGNORE', 'relocatable_obj', 'TRUE'            ],
  ...);

ls

$aref = $ccm->ls(@args);

Executes the ccm ls command with the given @args as parameters. The output (as formatted by the -format option) is split into lines. These are chomped and a reference to the resulting array of strings is returned.

If there was an error, undef is returned.

Note that you must pass every ccm ls argument or option as a single Perl argument.

If you are interested to obtain the value of several attributes, you should look at the "ls_arrayref" and "ls_hashref" methods that return this information in structured form. If you are only interested in the identity of the listed objects, you should look at the "ls_object" method.

ls_object

$aref = $ccm->ls_object($file_spec, @keywords);

Lists information about a file or the contents of a directory using the work area name $file_spec. Returns a reference to an array of corresponding VCS::CMSynergy::Objects.

Calling ls_object with an optional list of @keywords is only useful when ":cached_attributes" is in effect. In this case the returned VCS::CMSynergy::Objects have their attribute caches primed for the attributes listed in @keywords.

ls_arrayref

$aref = $ccm->ls_arrayref($file_spec, @keywords);

Lists the values of the built-in keywords or attributes supplied in @keywords for a file or the contents of a directory Returns a reference to an array of references, one per result row. Each reference points to an array containing the values of the keywords for that particular object (in the order given by @keywords).

If there was an error, undef is returned.

If the value of a keyword or an attribute is undefined or the attribute is not present, the actual value of the corresponding array element is undef (whereas ccm ls would print it as the string "<void>").

Note that the keyword or attribute names given in @keywords should not contain a leading %. Example:

my $result = $ccm->ls('foo', qw(displayname type modify_time);
foreach my $row (@$result)
{
  my ($displayname, $type, $modify_time) = @$row;
  print "$displayname ($type) last modified at $modify_time\n";
  ...
}

ls_hashref

$aref = $ccm->ls_hashref($file_spec, @keywords);

Lists the values of the built-in keywords or attributes supplied in @keywords for a file or the contents of a directory using the work area name $file_spec. Returns a reference to an array of references, one per result row. Each reference points to hash containing attribute and value pairs where the keys are @keywords.

If there was an error, undef is returned.

If the value of a keyword or an attribute is undefined or the attribute is not present, the actual value of the corresponding hash element is undef (whereas ccm ls would print it as the string "<void>").

Note that the keyword or attribute names given in @keywords should not contain a leading %. Example:

my $result = $ccm->ls_hashref('foo', qw(displayname type modify_time);
foreach my $row (@$result)
{
  print "$row->{displayname} last modified at $row->{modify_time}\n";
  ...
}

set

$value = $ccm->set($option);
$old_value = $ccm->set($option, $new_value);
$hash_ref = $ccm->set;

Get or set the value of an option.

In the first form, set returns the value of $option. If the option is unset, undef is returned (whereas ccm set would print "(unset)" in this case).

In the second form, the $option is set to $new_value, the previous value is returned. If $new_value is undef, $option is unset.

In the third form, a reference to a hash is returned. The hash consists of all currently defined options as keys and their respective values.

ccm_with_text_editor

($rc, $out, $err) = $ccm->ccm_with_text_editor($text_value, @cmd);

This is a convenience functions for executing a command that is sensitive to the value of the session option text_editor.

ccm_with_text_editor is useful in scripting ccm commands like ccm users. These commands usually open a temporary file generated by Synergy in a user-specified editor. Then the user edits the contents and save her changes. Finally, Synergy reads back the temporary file and does something with the (changed) contents.

ccm_with_text_editor does the following

  • creates a temporary file (using File::Temp::tempfile), say /tmp/a5Xghd, and writes the string $text_value into it,

  • saves the old value of text_editor and sets it to (on Unix)

    "cp /tmp/a5Xghd %filename"
  • executes

    $ccm->ccm(@cmd)

    which causes Synergy to accept $text_value as the "updated value" w.r.t. to command @cmd,

  • restores the value oc text_editor

  • finally removes the temporary file.

ccm_with_text_editor returns the same value as the inner "ccm" method, except when there is an error setting the new $value of $option.

ccm_addr

print "CCM_ADDR=", $ccm->ccm_addr;

Returns the session's RFC address.

database

$database = $ccm->database;

Returns the database path (as reported by ccm ps). The path is in canonical form (i.e. with a trailing "/db").

delimiter

$delim = $ccm->delimiter;

Returns the database delimiter.

dcm_enabled

Returns whether the database is DCM enabled.

dcm_delimiter

$delim = $ccm->dcm_delimiter;

Returns the DCM delimiter.

dcm_database_id

$dcm_id = $ccm->dcm_database_id;

Returns the DCM database id (ccm dcm -show -database_id) or the empty string if the database isn't DCM enabled.

user

Returns the session user (as reported by ccm ps).

default_project_instance

$instance  = $ccm->default_project_instance;

Returns the default instance for projects. Use it if you want to complete a project spec that only consists of the project's name and version:

$proj_spec .= ":project:" . $ccm->default_project_instance
  unless $proj_spec =~ /:project:/;

It is "1" if the database isn't DCM enabled, otherwise it is

$ccm->dcm_database_id . $ccm->dcm_delimiter . "1"

ping

if ($ccm->ping) { ... }

ping tests whether session $ccm is still alive (without causing an exception if it fails).

This could be used e.g. from a web application that keeps a pool of established Synergy sessions to deal with user requests: before invoking a command on a session the application must make sure that the session is still valid. If not, it will automatically create a new session.

object

$obj1 = $ccm->object($objectname);
$obj2 = $ccm->object($name, $version, $cvtype, $instance);

Create a VCS::CMSynergy::Object from either an objectname (sometimes called "object reference form" in Synergy documentation) in "name-version:cvtype:instance" format or the four parts specified separately.

This is just a wrapper for "new" in VCS::CMSynergy::Object. However, new requires the four parts of the objectname to be specified as separate arguments.

Note that no check is made whether the corresponding object really exists in the database, use "exists" in VCS::CMSynergy::Object for that.

baseline_object, cr_object, folder_object, project_object, task_object, tset_object

$baseline_obj = $ccm->baseline_object("toolkit_2.0_INT_1");
$cr_obj = $ccm->cr_object("42");              # when using Rational Change
$folder_obj = $ccm->folder_object("123");
$proj_obj = $ccm->project_object("toolkit-darcy");
$task_obj = $ccm->task_object("456#mydb");
$tset_obj = $ccm->tset_object("toolkit baselines");

# optional @keywords
$task_obj = $ccm->task_object("42", qw(status task_synopsis));

Create a VCS::CMSynergy::Object (with type "baseline", "probtrac", "folder", "project", "task" or "tset", resp.) from the displayname of a baseline, change request (AKA problem), folder, project, task or transfer set, resp.

If @keywords are specified, they are used as hints to prime the attribute cache of the returned VCS::CMSynergy::Object similar to the optional @keywords argument for "query_object". This is only useful when ":cached_attributes" is in effect.

object_other_version

$obj2 = $ccm->object_other_version($obj1, $version);

Returns a new VCS::CMSynergy::Object with the same name, cvtype and instance as $obj1 (which must be VCS::CMSynergy::Object), but with the version $version.

object_from_cvid

$obj = $ccm->object_from_cvid($cvid, @keywords);

Returns the VCS::CMSynergy::Object corresponding to the cvid $cvid (the internal primary identifier of a Synergy object). If the cvid doesn't exist, undef is returned.

This is handy, for example, when you're parsing certain Synergy log files that contain only the cvid and you want to identify the corresponding object.

The optional list of @keywords may be used to prime the attribute cache of the returned VCS::CMSynergy::Object (similar to the "query_object" method).

Note: object_from_cvid is implemented by something like

$ccm->property(objectname => "\@=$cvid")

object_from_proj_ref

$obj = $ccm->object_from_proj_ref($path, $proj_spec, @keywords);

Returns the VCS::CMSynergy::Object identified by workarea path $path in project $proj_spec. If no such object exists, undef is returned.

$proj_spec can be either a string (a Synergy "proj_spec") or a VCS::CMSynergy::Object.

$path can be either a string (a workarea-relative path, using the platforms native path separator) or an array ref of path components.

The optional list of @keywords may be used to prime the attribute cache of the returned VCS::CMSynergy::Object (similar to the "query_object" method).

Note: object_from_proj_ref is implemented by something like

$ccm->property(objectname => "$path\@$proj_spec")

base_admin, base_model, cs_admin, dcm_admin, cvtype, attype

$model = $ccm->base_model;
$project_type = $ccm->cvtype("project");

These are convenience methods for dealing with Synergy objects used to access the database model:

base_admin, base_model, cs_admin, dcm_admin

These return the VCS::CMSynergy::Object corresponding to the model objects:

base-1:admin:base
base-1:model:base
cs-1:admin:1                  # SYNERGY/Change enabled databases only
dcm-1:admin:dcm
cvtype, attype
$ccm->cvtype("foo")
$ccm->attype("foo")

These return the VCS::CMSynergy::Object corresponding to the model objects:

foo-1:cvtype:base
foo-1:attype:base

METHODS INHERITED FROM VCS::CMSynergy::Client

VCS::CMSynergy is derived from VCS::CMSynergy::Client, hence the following methods are inherited from the latter:

ccm_home
error, set_error
ccm_command, out, err
version
ps
status

Note: All these methods can be invoked on a session object or as class methods.

LOGGING

VCS::CMSynergy is "Log::Log4perl enabled". That means it logs via the Log::Log4perl module. All you have to do to turn logging on is something like the following in your program:

use VCS::CMsynergy;
use Log::Log4perl ':easy';
...
Log::Log4perl->easy_init($DEBUG);
...

This will turn on logging (to STDERR) at the $DEBUG level for all "Log::Log4perl enabled" modules used by your program. To turn on logging for the VCS::CMSynergy modules only use

Log::Log4perl->easy_init(
    { level => $DEBUG, category => "VCS::CMSynergy" });

See "Easy-Mode" in Log::Log4perl and "Stealth-loggers" in Log::Log4perl for more information.

For log level $TRACE all calls to the Synergy CLI are logged in gory detail (arguments, output to stdout and stderr, exit code and duration of execution).

SEE ALSO

VCS::CMSynergy::Client, VCS::CMSynergy::Object, VCS::CMSynergy::Project, VCS::CMSynergy::Users

AUTHOR

Roderich Schupp, argumentum GmbH <schupp@argumentum.de>

COPYRIGHT AND LICENSE

The VCS::CMSynergy modules are Copyright (c) 2001-2015 argumentum GmbH, http://www.argumentum.de. All rights reserved.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.