Utility to compare class and include file definitions between builds
The following code provides information about software builds. Read the original description below for information about the different methods:
///Description
///This class enables developers to compare class and INCLUDE routine definitions between software builds.
///It navigates through all aspects of a class definition and INCLUDE file code and uses a 32 bit crc on
///each element to produce a checksum for comparison purposes.
///The utility can simply return a checksum value, provides details at certain levels and output the
///results to a file for comparison if required.
///
///The class has three primary methods
///
///Class (class, details, filename)
///This method will provide a checksum for 1 class
///
///Package (package, details, filename)
///This method will checksum information for all classes that are members of a given package
///
///Namespace (details , filename)
///This method will provide a checksum for all non-system classes in a namespace and a checksum for all
///INCLUDE files in that namespace.
///This method is recommended as the preferred mechanism for comparing two software definitions in two
///different namespaces.
///
///The details flag operates with the following values
///0 - No details written, total checksum returned
///1 - Class total written, total checksum returned
///2 - Class total and element total written , total checksum returned
///3 - Class total,element total and named element total written , total checksum returned
Class objectscript.checkBuild extends (%RegisteredObject, %XML.Adaptor) [ClassType = "", Inheritance = right, ProcedureBlock]
{
/// Define the crcmode = 7 "A correct 32-bit CRC"
Parameter CRCMODE = 7;
/// Provides a checksum for 1 class based on summation of 32 bit CRC checking
///
Details
///
0 - No details written, total checksum returned
///
1 - Class total written, total checksum returned
///
2 - Class total and element total written , total checksum returned
///
3 - Class total,element total and named element total written , total checksum returned
///
ClassMethod Class(class As %String, details As %Integer = 0, filename As %String = "") As %Integer
{
//Open the file if requested
if filename'="" {
set file=..FileOpen(filename)
if file="Error" {
write "Unable to open file : ",filename
quit 0
}
}
else {
set file=""
}
//Checksum 1 class
set ccs=..CheckClass(class,details,file)
if file {
do ..FileClose(file)
}
quit ccs
}
ClassMethod CheckClass(class As %String, details As %Integer = 0, file = "") As %Integer
{
//Write out a blank line as a seperator followed by the classname
if details {
do ..Write("Class "_class,file)
}
//Initialize some iteration variables
set (element,name,node,sub,snode)=""
set selectivity=""
//Initialize checksum totals
//class|element|name
set cst="0|0|0"
//Process Header element checksum
for {
set element=$order(^oddDEF(class,element))
//Completed header information
if (element="")||(element'?.n) {
quit
}
//Eliminate date/timestamp from header - may vary
if (element'=63)&&(element'=64)&&(element'=69) {
set cst=..Add(cst,$zcrc(^(element),..#CRCMODE))
}
}
//Write out Header checksum details if wanted
if details>1 {
do ..Write($char(9)_"Header: "_$piece(cst,"|",2),file)
}
//Class elements
set element("a")="Attributes"
set element("f")="Foreign Keys"
set element("i")="Indexes"
set element("m")="Methods"
set element("p")="Parameters"
set element("q")="Queries"
set element("s")="Storage"
set element("t")="Triggers"
//Process all other element checksums
set element=""
//Iterate though the Elements
for {
//Get next element
set element=$order(element(element)) quit:element=""
//Reset Element checksum
set $piece(cst,"|",2)=0
//Iterate through Named Elements
for {
//Iterate through named elements
set name=$order(^oddDEF(class,element,name)) quit:name=""
//Reset Named element Checksum
set $piece(cst,"|",3)=0
//Iterate through nodes of Named Elements
for {
set node=$order(^oddDEF(class,element,name,node)) quit:node=""
//Add to checksums if data at this level
if ($data(^oddDEF(class,element,name,node))'=10)&&(node'=11) {
set cst=..Add(cst,$zcrc(^(node),..#CRCMODE))
}
//Iterate through sub-nodes of nodes of Named Elememts
for {
set sub=$order(^oddDEF(class,element,name,node,sub)) quit:sub=""
//Add to checksums
if $data(^oddDEF(class,element,name,node,sub))'=10 {
set cst=..Add(cst,$zcrc(^(sub),..#CRCMODE))
}
//Iterate through storage nodes
for {
set snode=$order(^oddDEF(class,element,name,node,sub,"V",snode)) quit:snode=""
//Add to checksums
set cst=..Add(cst,$zcrc(^(snode,21),..#CRCMODE))
//Update selectivity selectivity if selectivity exists in storage definition
if $data(^oddDEF(class,"s",name,"M")) {
set selectivity="*"
}
}
}
}
//Write out Named Element checksum details if requested
if details>2 {
do ..Write($char(9)_$char(9)_name_": "_$piece(cst,"|",3),file)
}
}
//Write out Element checksum details if requested
if details>1 {
do ..Write($char(9)_element(element)_": "_$piece(cst,"|",2)_selectivity,file)
//Reset selectivity indicator to ""
set selectivity=""
}
}
//Write out Class checksum details if requested
if details {
do ..Write($char(9)_"Checksum: "_$piece(cst,"|",1),file)
}
//Return Class checksum
quit $piece(cst,"|",1)
}
/// Provides a checksum for a package(s) based on summation of 32 bit CRC checking
///
"PackageName" - 1 package
///
"" - All packages in a namespace (excludes % - Sydtem classes)
///
Details
///
0 - No details written, total checksum returned
///
1 - Class total written, total checksum returned
///
2 - Class total and element total written , total checksum returned
///
3 - Class total,element total and named element total written , total checksum returned
///
ClassMethod Package(package As %String = "", details As %Integer = 0, filename As %String = "") As %Integer
{
//Open the file if requested
if filename'="" {
set file=..FileOpen(filename)
if file="Error" {
write "Unable to open file : ",filename
quit 0
}
}
else {
set file=""
}
//Checksum Package(s)
set pcs=..CheckPackage(package,details,file)
//Close the file
if file {
do ..FileClose(file)
}
//Return package checksum
quit pcs
}
ClassMethod CheckPackage(package As %String = "", details As %Integer = 0, file As %File = "") As %Integer
{
//Initiate package anc total checksum
set (tcs,pcs)=0
//Eliminate "%" system classes and checksum all packages if package=""
if package="" {
set package="@"
set cpackage=$piece($order(^oddDEF(package)),".",1)
}
//Iterate through a package/packages(s) sending classes off to CheckClass
set class=package
for {
set class=$order(^oddDEF(class))
if (class="")&&(package="@") {
if details {
do ..Write(cpackage_": "_pcs,file)
do ..Write("",file)
}
quit
}
elseif (package'="@")&&($piece(class,".",1)'=package) {
if details {
do ..Write(package_": "_pcs,file)
do ..Write("",file)
}
quit
}
elseif (package="@")&&($piece(class,".",1)'=cpackage) {
if details {
do ..Write(cpackage_": "_pcs,file)
do ..Write("",file)
}
set pcs=0
set cpackage=$piece(class,".",1)
}
else {
set ccs=..CheckClass(class,details,file)
set tcs=tcs+ccs
set pcs=pcs+ccs
}
}
//Write the package total checksum
if details {
do ..Write("Checksum: "_tcs,file)
}
quit tcs
}
/// Provides a checksum for a Namespace based on summation of 32 bit CRC checking
///
This includes all INCLUDE files for code generation
///
"" - All packages in a namespace (excludes % - Sydtem classes)
///
Details
///
0 - No details written, total checksum returned
///
1 - Class total written, total checksum returned
///
2 - Class total and element total written , total checksum returned
///
3 - Class total,element total and named element total written , total checksum returned
///
ClassMethod Namespace(details As %Integer = 0, filename As %String = "") As %Integer
{
//Open the file if requested
if filename'="" {
set file=..FileOpen(filename)
if file="Error" {
write "Unable to open file : ",filename
quit 0
}
}
else {
set file=""
}
//Go through the class packages first
set ncs=..CheckPackage("",details,file)
//Calculate the INCLUDE files
if details {
do ..Write("",file)
do ..Write("Include Files",file)
}
//Initialize INCLUDE files checksum
set ics=0
set routine="@",line=""
for {
set routine=$order(^rINC(routine)) quit:routine=""
//Initialize INCLUDE ROUTINE checksum
set rcs=0
//Iterate through the include file routines
for {
set line=$order(^rINC(routine,0,line)) quit:line=""
set rcs=rcs+$zcrc(^(line),..#CRCMODE)
set ics=ics+$zcrc(^(line),..#CRCMODE)
set ncs=ncs+$zcrc(^(line),..#CRCMODE)
}
if details>1 {
do ..Write($char(9)_routine_": "_rcs,file)
}
}
//Write out the INCLUDE files checksum
if details {
do ..Write("Checksum: "_ics,file)
}
//Write out the Namespace checksum
if details {
do ..Write("",file)
do ..Write("Namespace: "_ncs,file)
}
//Close the file
if file {
do ..FileClose(file)
}
//Return the namespace checksum
quit ncs
}
ClassMethod FileOpen(filename As %String) As %File
{
set file=##class(%File).%New(filename)
set ok=file.Open("WNS")
if 'ok {
do $system.OBJ.DisplayError(ok)
quit "Error"
}
else {
quit file
}
}
ClassMethod FileClose(file As %File)
{
do file.Close()
quit
}
ClassMethod Write(string As %String, file As %File)
{
if file {
do file.WriteLine(string)
}
write !,string
quit
}
ClassMethod Add(cst As %String, crc As %Integer) As %String
{
set $piece(cst,"|",1)=$piece(cst,"|",1)+crc
set $piece(cst,"|",2)=$piece(cst,"|",2)+crc
set $piece(cst,"|",3)=$piece(cst,"|",3)+crc
quit cst
}
}
Here's a link to the code on GitHub: https://github.com/intersystems-community/code-snippets/blob/master/src/...