Updating macros in a C file using perl

Viewed 89

I want to update a release number in a C file where it is present in the form of a macro. I want to implement this in perl. I have done a small implementation but I'm eager to understand if this is the right solution or it can be optimsed further.

The C file (input file: test_file.c) is as given below:

INPUT_FILE (test_file.c)

#include "header_file.h"

#define ALPHA_MAJOR_VERSION    100
#define ALPHA_MINOR_VERSION    20
#define BETA_MAJOR_VERSION      1
#define BETA_MINOR_VERSION      2
#define DELTA_BRANCH_VERSION    3

#if ((ALPHA_MAJOR_VERSION != SOME_VERSION) || \
     (ALPHA_MINOR_VERSION != SOME_OTHER_VERSION))
#error Versions do not match
    typedef int fix_this_compiler_error;
#else
    typedef int fix_this_compiler_error;
#endif

Lets say that the release number is 101p1p2p3p4, I want to split this release number and update the macros in the C file as shown below:

#define ALPHA_MAJOR_VERSION    101
#define ALPHA_MINOR_VERSION     1
#define BETA_MAJOR_VERSION      2
#define BETA_MINOR_VERSION      3
#define DELTA_BRANCH_VERSION    4

Here in the release number 101p1p2p3p4 the value after every p is a major/minor version for the release and same needs to be updated in the C file. So the desired output look something like this:

DESIRED_OUTPUT

#include "header_file.h"

#define ALPHA_MAJOR_VERSION    101
#define ALPHA_MINOR_VERSION    1
#define BETA_MAJOR_VERSION     2
#define BETA_MINOR_VERSION     3
#define DELTA_BRANCH_VERSION   4

#if ((ALPHA_MAJOR_VERSION != SOME_VERSION) || \
     (ALPHA_MINOR_VERSION != SOME_OTHER_VERSION))
#error Versions do not match
    typedef int fix_this_compiler_error;
#else
    typedef int fix_this_compiler_error;
#endif

MY_IMPLEMENTATION

#!/usr/bin/perl

use strict;
use warnings;

my $release_num = "101p1p2p3p4";
my @array = split(/p/, $release_num);
my @versions;

for (my $i=0; $i<scalar(@array); $i++)
{
    $versions[$i] = $array[$i];
}
print "array is : @versions \n";
print "0 : $versions[0] \n";
print "1 : $versions[1] \n";
print "2 : $versions[2] \n";
print "3 : $versions[3] \n";
print "4 : $versions[4] \n";

my $file = '/path/to/the/file/test_file.c';

`sed -i '/^\#define ALPHA_MAJOR_VERSION/ c\\#define ALPHA_MAJOR_VERSION      $versions[0]' $file`;
`sed -i '/^\#define ALPHA_MINOR_VERSION/ c\\#define ALPHA_MINOR_VERSION      $versions[1]' $file`;
`sed -i '/^\#define BETA_MAJOR_VERSION/ c\\#define BETA_MAJOR_VERSION      $versions[2]' $file`;
`sed -i '/^\#define BETA_MINOR_VERSION/ c\\#define BETA_MINOR_VERSION      $versions[3]' $file`;
`sed -i '/^\#define DELTA_BRANCH_VERSION/ c\\#define DELTA_BRANCH_VERSION      $versions[4]' $file`;

With my implementation I'm able to get the desired output as shown above , but want to understand that this is the right way to solve this problem or there can be a better method.

I asked similar question before but it was closed by moderators as i didn't share my work. I hope this question will not be closed because I've put an effort to propose a solution. Thanks!

Link to closed question: How can i update a macro in a C file using perl

1 Answers

You can use something like this script.

#!/usr/bin/env perl
use strict;
use warnings;

my $release_num = "101p1p2p3p4";
my @versions    = split(/p/, $release_num);
my $file        = "test_file.c";
my $backext     = "";

{
    local ($^I, @ARGV) = ($backext, $file);
    while (<>) {
        s/#define ALPHA_MAJOR_VERSION\s+\K\d+/$versions[0]/;
        s/#define ALPHA_MINOR_VERSION\s+\K\d+/$versions[1]/;
        s/#define BETA_MAJOR_VERSION\s+\K\d+/$versions[2]/;
        s/#define BETA_MINOR_VERSION\s+\K\d+/$versions[3]/;
        s/#define DELTA_BRANCH_VERSION\s+\K\d+/$versions[4]/;
        print;
    }
}
1;

Running external commands (as you did with sed) is unnecessary. External commands add the expensive overhead of process forking when Perl can accomplish the task that the external command would.

The $^I turns on in-place editing. It emulates Perl's -i switch. The file or files to be acted upon come from @ARGV as read by the <> operator. For good practice, we localize action with these entities by creating a block with bounding braces and declare local variables. If you set $backext an empty string, no backup copy will be preserved, otherwise the string will be appended to the original file name and the original file preserved.

The use of \K in the substitution keeps the text to the left of the match. This allows the original space between the define and the value to be preserved.

I use the final 1; as a marker for the end of a script's code. It is immaterial unless you are writing a package or module. In that case, a the file must return true as its last statement for a require or use directive to be successful.

Related