Quartus® II Tcl Example: Increment Version Number in File



One way of maintaining a version number in your project is to increment a number in a file during every compilation. To perform this task, a script must parse the file to locate the number, increment it, then rewrite the file with the new number. If the file is short and contains little else besides the number, the script can simply write a new file from scratch every time it runs. However, if the file is a long design file, it may be more practical to edit the line with the version number instead of writing out a new file from scratch.

Locate the Number

The script uses a regular expression to locate the number in the file. The line with the number should have a unique format, otherwise multiple lines can match the regular expression. One way to make a unique format is to include a unique comment on the line with the number. The regular expression can include that unique comment. The following is an example of a line in a design file with a version number line that includes a special comment, and the regular expression Tcl command that matches it.

Design File Line

data_out <= 16'h41; // Design Version Number

Regular Expression

regexp {^\s+data_out <= \d+'h([[:xdigit:]]+); // Design Version Number$} \ $line match version_number

The ([[:xdigit:]]+) pattern matches a hexadecimal number with at least one digit and saves it in a match variable named version_number.

Increment the Number

You can increment hexadecimal numbers in Tcl. In Tcl, hexadecimal numbers are indicated with a leading 0x. If your version number does not begin with 0x, it may be easier to convert the number to base 10 before you increment it. For a hexadecimal number, the following commands show an example of converting a hexadecimal string to base 10, incrementing it, and converting it back to hexadecimal.

set hex_value "AA"
scan $hex_value "%x" decimal_value
incr decimal_value
set new_hex_value [format "%X" $decimal_value]
# $new_hex_value is now "AB"

Rewrite the File

If the design file is short and contains information only about the version number, you can use a series of puts commands to write out a new file with the incremented number.

If the design file is long, it is usually easier to change only the line with the version number, because hard coding the file contents in the Tcl script would be impractical.

The following procedure parses through a design file and updates the line with the version information. Lines that do not match the regular expression are written to a new file, unmodified. The line that does match the regular expression has the version number incremented, then is written to the new file.

proc update_version_number { input_file output_file} {

    # If the input file can't be opened, return an error.
    if { [catch {open $input_file} input] } {
        return -code error $input

    # If the output file can't be opened, return an error
    if { [catch {open $output_file w} output] } {
        return -code error $output

    # Read through the input file a line at a time
    while {-1 != [gets $input line] } {

        # This regular expression is specific to the design
        # file line in near the top of the web page.
        # You must change it as appropriate for your file.
        if { [regexp {^\s+data_out <= \d+'h([[:xdigit:]]+); // Design Version Number$} \ $line match version_number] } { # Convert the hexadecimal version number to base ten and increment it. scan $version_number "%x" decimal_value incr decimal_value set new_version_number [format "%X" $decimal_value] # Substitute the new version number in for the old one regsub h${version_number} $line h${new_version_number} line } # Write out the line to the new file puts $output $line } close $input close $output }   </pre>

In a Tcl script, you can call the procedure as shown in the following example. The example is written assuming that you run the Tcl script at a system command prompt, and provide the file name to update as an argument to the script.

set file_name [lindex $quartus(args) 0]
set output_file_name ${file_name}.updated_version_number

if { [catch { update_version_number $file_name $output_file_name } res] } {
    post_message -type critical_warning "Could not update version number: $res"
} else {

    if { [catch { file rename -force $output_file_name $file_name } res ] } {
        post_message -type critical_warning \
            "Could not update version number: $res"