| #!/usr/bin/perl |
| |
| # |
| # Copyright (c) 2020 Project CHIP Authors |
| # Copyright (c) 2019 Google LLC. |
| # Copyright (c) 2014-2017 Nest Labs, Inc. |
| # All rights reserved. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| |
| # |
| # Description: |
| # This file implements a script to parse a version number from |
| # the command line, split it into its constituent components, and |
| # then auto-generate a C-language header file containing |
| # preprocessor mnemonics for those components. |
| # |
| |
| use strict 'refs'; |
| |
| use File::Basename; |
| use Getopt::Long qw(:config gnu_getopt); |
| use POSIX; |
| use Time::Piece; |
| |
| # Global Variables |
| |
| # Program name |
| |
| my($program); |
| |
| # Decoded options |
| |
| my(%options); |
| |
| # |
| # PrintUsage() |
| # |
| # Description: |
| # This routine prints out the proper command line usage for this |
| # program |
| # |
| # Input(s): |
| # status - Flag determining what usage information will be printed and what |
| # the exit status of the program will be after the information is |
| # printed. |
| # |
| # Output(s): |
| # N/A |
| # |
| # Returns: |
| # This subroutine does not return. |
| # |
| sub PrintUsage { |
| my($status) = $_[0]; |
| |
| print(STDERR "Usage: $program [ options ... ] [ -o OUTPUT ] <version>\n"); |
| |
| if ($status != EXIT_SUCCESS) { |
| print(STDERR "Try `$program --help' for more information.\n"); |
| } |
| |
| if ($status != EXIT_FAILURE) { |
| my($usage) = |
| " |
| General Options: |
| |
| -h, --help Display this information. |
| |
| Input and Output Options: |
| |
| -o, --output=PATH Specify PATH as the output file to write with the |
| generated ouput (default: standard output). |
| "; |
| |
| print(STDERR $usage); |
| } |
| |
| exit($status); |
| } |
| |
| # |
| # DecodeOptions() |
| # |
| # Description: |
| # This routine steps through the command-line arguments, parsing out |
| # recognzied options. |
| # |
| # Input(s): |
| # N/A |
| # |
| # Output(s): |
| # N/A |
| # |
| # Returns: |
| # N/A |
| # |
| sub DecodeOptions { |
| if (!&GetOptions(\%options, |
| "help|h", |
| "output|o=s", |
| )) { |
| print(STDERR "option parsing failed!\n"); |
| PrintUsage(EXIT_FAILURE); |
| } |
| |
| if ($options{"help"}) { |
| PrintUsage(EXIT_SUCCESS); |
| } |
| |
| # There should be exactly one additional command line argument. |
| |
| if (scalar(@ARGV) != 1) { |
| PrintUsage(EXIT_FAILURE); |
| } |
| |
| return; |
| } |
| |
| # |
| # ParseVersion() |
| # |
| # Description: |
| # This routine accepts |
| # recognzied options. |
| # |
| # Input(s): |
| # version - A scalar containing the version to parse as a string. |
| # major - A reference to a scalar in which to store the major |
| # component of the version, as a scalar. |
| # minor - A reference to a scalar in which to store the minor |
| # component of the version, as a scalar. |
| # patch - A reference to a scalar in which to store the patch |
| # component of the version, as a scalar. |
| # extra - A reference to a scalar in which to store the "extra" |
| # component of the version, as a string. |
| # |
| # Output(s): |
| # major - A reference to the major component of the version, as |
| # a scalar. |
| # minor - A reference to the minor component of the version, as |
| # a scalar. |
| # patch - A reference to the patch component, if present, of the |
| # version, as a scalar. |
| # extra - A reference to the "extra" component, if present, of |
| # version, as a string. |
| # |
| # Returns: |
| # N/A |
| # |
| sub ParseVersion { |
| my($version) = $_[0]; |
| my($major_ref, $minor_ref, $patch_ref, $extra_ref) = @_[1..$#_]; |
| my(@components); |
| |
| # Attempt to split the version into two required components: major |
| # and minor. Patch and "extra" components may be present, those |
| # will be handled subseqeuntly. |
| |
| @components = split(/\./, ${version}, 2); |
| |
| if (@components == 2) { |
| my($first, $second, $third); |
| |
| # Unconditionally parse the major component, stripping leading |
| # white space and any trailing non-digit characters. |
| |
| ${first} = shift(@components); |
| ($${major_ref}, $${extra_ref}) = (${first} =~ m/^\s*(\d+).*$/); |
| |
| # At this point, we have either a minor component and some |
| # optional "extra" or a minor component, patch component, and |
| # some optional "extra". |
| |
| ${second} = shift(@components); |
| ($${minor_ref}, $${extra_ref}) = (${second} =~ m/^\s*(\d+)(.*)$/); |
| |
| if ($${extra_ref} =~ m/^\./) { |
| ($${patch_ref}, $${extra_ref}) = ($${extra_ref} =~ m/^\.\s*(\d+)(.*)$/); |
| } |
| } |
| } |
| |
| # |
| # Main program body |
| # |
| |
| { |
| my($oh, $format); |
| |
| # Set the program name |
| |
| $program = basename($0); |
| |
| # Parse options from the command line |
| |
| DecodeOptions(); |
| |
| # At this point, the version to parse should be the only argument. |
| |
| my($chip_version) = @ARGV[0]; |
| |
| # Initialize the output stream. |
| |
| if ($options{"output"}) { |
| if (open($oh, "+>$options{output}") == 0) { |
| print(STDERR "Could not open \"$options{output}\" for append.\n"); |
| exit(EXIT_FAILURE); |
| } |
| |
| } else { |
| $oh = \*STDOUT; |
| |
| } |
| |
| # Establish the copyright year and script base name scalar variables |
| # for substitution in the here document output. |
| |
| my($first_year) = 2018; |
| my($current_year) = Time::Piece->new()->year; |
| my($copyright_year) = "${first_year}"; |
| |
| if (${first_year} < ${current_year}) { |
| ${copyright_year} .= "-${current_year}"; |
| } |
| |
| # Establish some reasonable version component defaults. |
| |
| my($chip_major) = 0; |
| my($chip_minor) = 0; |
| my($chip_patch) = 0; |
| my($chip_extra) = undef; |
| |
| # Attempt to parse the version into its constituent components. |
| |
| ParseVersion(${chip_version}, \${chip_major}, \${chip_minor}, \${chip_patch}, \${chip_extra}); |
| |
| # Canonically recompose the version from its components. |
| |
| if (${chip_patch} == 0) { |
| ${chip_version} = sprintf("%u.%u%s", ${chip_major}, ${chip_minor}, ${chip_extra}); |
| } else { |
| ${chip_version} = sprintf("%u.%u.%u%s", ${chip_major}, ${chip_minor}, ${chip_patch}, ${chip_extra}); |
| } |
| |
| # Generate the output file as a Perl "here document" with scalar |
| # variable interpolation (escaping where necessary). |
| |
| my($template) = |
| " |
| /* |
| * |
| * Copyright (c) ${copyright_year} Project CHIP Authors |
| * All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the \"License\"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an \"AS IS\" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * \@file |
| * This file defines constants and macros for introspecting and |
| * manipulating CHIP versions. |
| * |
| * !!! WARNING !!! WARNING !!! WARNING !!! WARNING !!! |
| * |
| * DO NOT EDIT THIS FILE! This file is automatically-generated by |
| * the '${program}' script. |
| * |
| * The constants and macros defined here may be used to, for , |
| * example, conditionally-compile older, newer, or changed CHIP |
| * APIs based on the CHIP version. For example: |
| * |
| * \@code |
| * #if CHIP_VERSION_CODE >= CHIP_VERSION_CODE_ENCODE(1, 5, 0) |
| * ... |
| * #else |
| * ... |
| * #endif |
| * \@endcode |
| * |
| */ |
| |
| #ifndef CHIP_VERSION_H_ |
| #define CHIP_VERSION_H_ |
| |
| #define _CHIP_VERSION_CODE_MAJOR_WIDTH 8 |
| #define _CHIP_VERSION_CODE_MINOR_WIDTH 8 |
| #define _CHIP_VERSION_CODE_PATCH_WIDTH 8 |
| |
| #define _CHIP_VERSION_CODE_MAJOR_MASK ((1 << _CHIP_VERSION_CODE_MAJOR_WIDTH) - 1) |
| #define _CHIP_VERSION_CODE_MINOR_MASK ((1 << _CHIP_VERSION_CODE_MINOR_WIDTH) - 1) |
| #define _CHIP_VERSION_CODE_PATCH_MASK ((1 << _CHIP_VERSION_CODE_PATCH_WIDTH) - 1) |
| |
| #define _CHIP_VERSION_CODE_MAJOR_SHIFT 24 |
| #define _CHIP_VERSION_CODE_MINOR_SHIFT 16 |
| #define _CHIP_VERSION_CODE_PATCH_SHIFT 8 |
| |
| /** |
| * \@def CHIP_VERSION_CODE_ENCODE(major, minor, patch) |
| * |
| * \@brief |
| * Encode a CHIP version code from its constituent \@a major, \@a minor, and \@a patch |
| * components. |
| * |
| * This macro may be used in conjunction with CHIP_VERSION_CODE to, for |
| * example, conditionally-compile older, newer, or changed CHIP APIs based |
| * on the CHIP version. For example: |
| * |
| * \@code |
| * #if CHIP_VERSION_CODE >= CHIP_VERSION_CODE_ENCODE(1, 5, 0) |
| * ... |
| * #else |
| * ... |
| * #endif |
| * \@endcode |
| * |
| */ |
| #define CHIP_VERSION_CODE_ENCODE(major, minor, patch) \\ |
| ((((major) & _CHIP_VERSION_CODE_MAJOR_MASK) << _CHIP_VERSION_CODE_MAJOR_SHIFT) | \\ |
| (((minor) & _CHIP_VERSION_CODE_MINOR_MASK) << _CHIP_VERSION_CODE_MINOR_SHIFT) | \\ |
| (((patch) & _CHIP_VERSION_CODE_PATCH_MASK) << _CHIP_VERSION_CODE_PATCH_SHIFT)) |
| |
| /** |
| * \@def CHIP_VERSION_CODE_DECODE_MAJOR(code) |
| * |
| * \@brief |
| * Decode a CHIP major version component from a CHIP version \@a code. |
| * |
| */ |
| #define CHIP_VERSION_CODE_DECODE_MAJOR(code) (((code) >> _CHIP_VERSION_CODE_MAJOR_SHIFT) & _CHIP_VERSION_CODE_MAJOR_MASK) |
| |
| /** |
| * \@def CHIP_VERSION_CODE_DECODE_MINOR(code) |
| * |
| * \@brief |
| * Decode a CHIP minor version component from a CHIP version \@a code. |
| * |
| */ |
| #define CHIP_VERSION_CODE_DECODE_MINOR(code) (((code) >> _CHIP_VERSION_CODE_MINOR_SHIFT) & _CHIP_VERSION_CODE_MINOR_MASK) |
| |
| /** |
| * \@def CHIP_VERSION_CODE_DECODE_PATCH(code) |
| * |
| * \@brief |
| * Decode a CHIP patch version component from a CHIP version \@a code. |
| * |
| */ |
| #define CHIP_VERSION_CODE_DECODE_PATCH(code) (((code) >> _CHIP_VERSION_CODE_PATCH_SHIFT) & _CHIP_VERSION_CODE_PATCH_MASK) |
| |
| /** |
| * \@def CHIP_VERSION_MAJOR |
| * |
| * \@brief |
| * The CHIP version major component, as an unsigned integer. |
| * |
| */ |
| #define CHIP_VERSION_MAJOR ${chip_major} |
| |
| /** |
| * \@def CHIP_VERSION_MINOR |
| * |
| * \@brief |
| * The CHIP version minor component, as an unsigned integer. |
| * |
| */ |
| #define CHIP_VERSION_MINOR ${chip_minor} |
| |
| /** |
| * \@def CHIP_VERSION_PATCH |
| * |
| * \@brief |
| * The CHIP version patch component, as an unsigned integer. |
| * |
| */ |
| #define CHIP_VERSION_PATCH ${chip_patch} |
| |
| /** |
| * \@def CHIP_VERSION_EXTRA |
| * |
| * \@brief |
| * The CHIP version extra component, as a quoted C string. |
| * |
| */ |
| #define CHIP_VERSION_EXTRA \"${chip_extra}\" |
| |
| /** |
| * \@def CHIP_VERSION_STRING |
| * |
| * \@brief |
| * The CHIP version, as a quoted C string. |
| * |
| */ |
| #define CHIP_VERSION_STRING \"${chip_version}\" |
| |
| /** |
| * \@def CHIP_VERSION_CODE |
| * |
| * \@brief |
| * The CHIP version, including the major, minor, and patch components, |
| * encoded as an unsigned integer. |
| * |
| * This macro may be used in conjunction with CHIP_VERSION_CODE_ENCODE |
| * to, for example, conditionally-compile older, newer, or changed CHIP |
| * APIs based on the CHIP version. For example: |
| * |
| * \@code |
| * #if CHIP_VERSION_CODE >= CHIP_VERSION_CODE_ENCODE(1, 5, 0) |
| * ... |
| * #else |
| * ... |
| * #endif |
| * \@endcode |
| * |
| */ |
| #define CHIP_VERSION_CODE CHIP_VERSION_CODE_ENCODE( \\ |
| CHIP_VERSION_MAJOR, \\ |
| CHIP_VERSION_MINOR, \\ |
| CHIP_VERSION_PATCH \\ |
| ) |
| |
| #endif /* CHIP_VERSION_H_ */ |
| "; |
| |
| print(${oh} ${template}); |
| |
| exit(EXIT_SUCCESS); |
| } |
| |
| |
| |
| |
| |
| |