From ea6b4c99ec32127619b6e5c3a5ff58be51aa4829 Mon Sep 17 00:00:00 2001 From: Matthew Fernandez Date: Sun, 10 Apr 2022 19:43:04 -0700 Subject: [PATCH] port colortbl.h generation script to Python Note that this does not introduce an extra build dependency in any of the three build systems: 1. Autotools: these steps are done during construction of the portable source tarball, during which Python 3 is already required (see autogen.sh). 2. CMake: Python 3 is already required by and used in the top level CMakeLists.txt. 3. MS Build: Python 3 is already used in version generation in lib/version/version.vcxproj. This change is motivated by the goal of removing a dependency on Awk. This commit removes the last remaining Awk usage in all three build systems, as well as removing dependencies on `type` and `sort` in the MS Build build system. This commit removes the generation of the intermediate artifacts color_lib, color_lib-sort, and color_lib-temp. Instead, we generate only the final artifact, colortbl.h. This consolidation of logic into Python is part of what allows the removal of dependencies described above. Gitlab: #2118 --- .gitignore | 3 - Makefile.am | 2 +- awk/colortbl.awk | 45 ------------- cmake/generate_color_lib.cmake.in | 14 ---- lib/common/CMakeLists.txt | 24 ++----- lib/common/Makefile.am | 16 ++--- lib/common/make_colortbl.py | 103 ++++++++++++++++++++++++++++++ lib/gvc.vcxproj | 6 +- 8 files changed, 120 insertions(+), 93 deletions(-) delete mode 100644 awk/colortbl.awk delete mode 100644 cmake/generate_color_lib.cmake.in create mode 100644 lib/common/make_colortbl.py diff --git a/.gitignore b/.gitignore index 34e6b259e..be6e8fb32 100644 --- a/.gitignore +++ b/.gitignore @@ -141,9 +141,6 @@ lib/cgraph/grammar.h lib/cgraph/grammar.output lib/cgraph/scan.c lib/common/brewer_lib -lib/common/color_lib -lib/common/color_lib-sort -lib/common/color_lib-temp lib/common/colortbl.h lib/common/htmlparse.c lib/common/htmlparse.h diff --git a/Makefile.am b/Makefile.am index 97a34d3a5..e96449f16 100644 --- a/Makefile.am +++ b/Makefile.am @@ -33,7 +33,7 @@ EXTRA_DIST = $(html) graphviz.spec \ autogen.sh config/depcomp config/config.rpath \ builddate.h \ m4/README m4/lib-ld.m4 m4/lib-link.m4 m4/lib-prefix.m4 \ - graphviz.7 Doxyfile.in Doxyfile awk \ + graphviz.7 Doxyfile.in Doxyfile \ config/config_perl.pl \ config/config_ruby.rb config/config_tcl.tcl dot.demo plugin.demo \ macosx windows debian redhat graphviz.sln diff --git a/awk/colortbl.awk b/awk/colortbl.awk deleted file mode 100644 index ab490f485..000000000 --- a/awk/colortbl.awk +++ /dev/null @@ -1,45 +0,0 @@ -# -# /************************************************************************* -# * Copyright (c) 2011 AT&T Intellectual Property -# * All rights reserved. This program and the accompanying materials -# * are made available under the terms of the Eclipse Public License v1.0 -# * which accompanies this distribution, and is available at -# * http://www.eclipse.org/legal/epl-v10.html -# * -# * Contributors: Details at http://www.graphviz.org/ -# *************************************************************************/ - -function rgb_to_hsv(r,g,b) { - r = r / 255.0; g = g / 255.0; b = b / 255.0; - max = r; if (max < g) max = g; if (max < b) max = b; - min = r; if (min > g) min = g; if (min > b) min = b; - v = max; - if (max != 0) s = (max - min) / max; - else s = 0; - if (s == 0) h = 0; - else { - delta = max - min; - rc = (max - r)/delta; - gc = (max - g)/delta; - bc = (max - b)/delta; - if (r == max) h = bc - gc; - else { - if (g == max) h = 2.0 + (rc - bc); - else h = 4.0 + (gc - rc); - } - h = h * 60.0; - if (h < 0.0) h = h + 360.0; - } - h = h / 360.0 * 255.0; - s = s * 255.0; - v = v * 255.0; -} - -BEGIN { gsub("\\.","_",s); printf("static hsvrgbacolor_t color_lib[] = {\n",s); } -/^$/ { next; } -/^#/ { next; } - { - rgb_to_hsv($2,$3,$4); - printf("{\"%s\",%d,%d,%d,%d,%d,%d,%d},\n",$1,h,s,v,$2,$3,$4,$5); - } -END { printf("};\n"); } diff --git a/cmake/generate_color_lib.cmake.in b/cmake/generate_color_lib.cmake.in deleted file mode 100644 index f32b80a37..000000000 --- a/cmake/generate_color_lib.cmake.in +++ /dev/null @@ -1,14 +0,0 @@ -file(READ @CMAKE_CURRENT_BINARY_DIR@/svgcolor_lib svgcolor_lib) -file(READ @CMAKE_CURRENT_BINARY_DIR@/brewer_lib brewer_lib) -file(READ @CMAKE_CURRENT_SOURCE_DIR@/color_names color_names) - -# Do not ignore empty elements in list -cmake_policy(SET CMP0007 NEW) - -# Make it a list for sorting -string(REPLACE "\n" ";" color_lib ${svgcolor_lib} ${brewer_lib} ${color_names}) -file(WRITE @CMAKE_CURRENT_BINARY_DIR@/color_lib-temp "${color_lib}") -list(SORT color_lib) -file(WRITE @CMAKE_CURRENT_BINARY_DIR@/color_lib-sort "${color_lib}") -string(REGEX REPLACE ";" "\\n" color_lib "${color_lib}") -file(WRITE @CMAKE_CURRENT_BINARY_DIR@/color_lib ${color_lib}) diff --git a/lib/common/CMakeLists.txt b/lib/common/CMakeLists.txt index fef1d353f..c9e15e95d 100644 --- a/lib/common/CMakeLists.txt +++ b/lib/common/CMakeLists.txt @@ -22,29 +22,19 @@ add_custom_command( ${CMAKE_CURRENT_BINARY_DIR}/brewer_lib COMMENT "generate Brewer color library" ) -configure_file( - "${CMAKE_SOURCE_DIR}/cmake/generate_color_lib.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/generate_color_lib.cmake" - @ONLY -) add_custom_command( - OUTPUT color_lib + OUTPUT common/colortbl.h DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/svgcolor_lib ${CMAKE_CURRENT_BINARY_DIR}/brewer_lib ${CMAKE_CURRENT_SOURCE_DIR}/color_names - COMMAND ${CMAKE_COMMAND} -P - ${CMAKE_CURRENT_BINARY_DIR}/generate_color_lib.cmake - COMMENT "generate amalgamated color library" -) -add_custom_command( - OUTPUT common/colortbl.h - DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/color_lib - ${CMAKE_SOURCE_DIR}/awk/colortbl.awk + ${CMAKE_CURRENT_SOURCE_DIR}/make_colortbl.py COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/common" - COMMAND ${AWK_EXECUTABLE} -f ${CMAKE_SOURCE_DIR}/awk/colortbl.awk - ${CMAKE_CURRENT_BINARY_DIR}/color_lib - >${CMAKE_CURRENT_BINARY_DIR}/common/colortbl.h + COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/make_colortbl.py + ${CMAKE_CURRENT_BINARY_DIR}/brewer_lib + ${CMAKE_CURRENT_BINARY_DIR}/svgcolor_lib + ${CMAKE_CURRENT_SOURCE_DIR}/color_names + ${CMAKE_CURRENT_BINARY_DIR}/common/colortbl.h COMMENT "generate color table" ) diff --git a/lib/common/Makefile.am b/lib/common/Makefile.am index 7ea1d7238..6e64b397c 100644 --- a/lib/common/Makefile.am +++ b/lib/common/Makefile.am @@ -36,12 +36,10 @@ libcommon_C_la_LIBADD = \ colxlate.o colxlate.lo : colortbl.h -colortbl.h : color_lib - $(AWK) -f $(top_srcdir)/awk/colortbl.awk color_lib > colortbl.h - -# ensure color names are properly sorted for bsearch operation -color_lib : brewer_lib svgcolor_lib $(top_srcdir)/lib/common/color_names - cat brewer_lib svgcolor_lib $(top_srcdir)/lib/common/color_names | LC_ALL=C $(SORT) > color_lib +colortbl.h: brewer_lib svgcolor_lib $(top_srcdir)/lib/common/color_names \ + $(top_srcdir)/lib/common/make_colortbl.py + python3 $(top_srcdir)/lib/common/make_colortbl.py brewer_lib svgcolor_lib \ + $(top_srcdir)/lib/common/color_names colortbl.h brewer_lib: $(top_srcdir)/lib/common/brewer_colors \ $(top_srcdir)/lib/common/make_brewer_lib.py @@ -57,12 +55,12 @@ htmlparse.o htmlparse.lo: htmlparse.c htmlparse.h htmlparse.c htmlparse.h: $(top_srcdir)/lib/common/htmlparse.y $(YACC) -Wno-yacc -dv $(top_srcdir)/lib/common/htmlparse.y -o htmlparse.c -DISTCLEANFILES = brewer_lib color_lib colortbl.h \ +DISTCLEANFILES = brewer_lib colortbl.h \ htmlparse.[ch] svgcolor_lib EXTRA_DIST = README.imap \ htmlparse.c htmlparse.h \ entities.html entities.tcl \ brewer_colors brewer_lib svgcolor_names svgcolor_lib \ - color_names color_lib colortbl.h \ - make_brewer_lib.py make_svgcolor_lib.py + color_names colortbl.h \ + make_brewer_lib.py make_colortbl.py make_svgcolor_lib.py diff --git a/lib/common/make_colortbl.py b/lib/common/make_colortbl.py new file mode 100644 index 000000000..e6a01bf6d --- /dev/null +++ b/lib/common/make_colortbl.py @@ -0,0 +1,103 @@ +#!/usr/bin/python3 + +""" +(brewer_lib, svgcolor_lib, color_names) → colortbl.h generator +""" + +import argparse +import locale +import re +import sys +from typing import List, Tuple + +def rgb_to_hsv(r: int, g: int, b: int) -> Tuple[int, int, int]: + """transform an RGB color into HSV format""" + + r /= 255.0 + g /= 255.0 + b /= 255.0 + + max_ = max((r, g, b)) + min_ = min((r, g, b)) + + v = max_ + + if max_ != 0: + s = (max_ - min_) / max_ + else: + s = 0 + + if s == 0: + h = 0 + else: + delta = max_ - min_ + rc = (max_ - r) / delta + gc = (max_ - g) / delta + bc = (max_ - b) / delta + if r == max_: + h = bc - gc + elif g == max_: + h = 2.0 + (rc - bc) + else: + h = 4.0 + (gc - rc) + h *= 60.0 + if h < 0.0: + h += 360.0 + + return int(h / 360.0 * 255.0), int(s * 255.0), int(v * 255.0) + +def main(args: List[str]) -> int: + """entry point""" + + # avoid sorting issues due to locale differences + locale.setlocale(locale.LC_ALL, "C") + + # parse command line arguments + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("brewer_lib", type=argparse.FileType("rt"), + help="input Brewer color table entries") + parser.add_argument("svgcolor_lib", type=argparse.FileType("rt"), + help="input SVG color table entries") + parser.add_argument("color_names", type=argparse.FileType("rt"), + help="input other color table entries") + parser.add_argument("output", type=argparse.FileType("wt"), + help="output color table C header") + options = parser.parse_args(args[1:]) + + options.output.write("static hsvrgbacolor_t color_lib[] = {\n") + + # collect all color entries + entries = options.brewer_lib.readlines() + options.svgcolor_lib.readlines() \ + + options.color_names.readlines() + + # sort these so `bsearch` for a color will work correctly + entries = sorted(entries, key=locale.strxfrm) + + for entry in entries: + + # ignore blank lines + if entry.strip() == "": + continue + + # extract fields of the entry + m = re.match(r"(?P\S+) (?P\d+) (?P\d+) (?P\d+) (?P\d+)", entry) + assert m is not None, f'unexpected entry format "{entry}"' + + # transform an RGB color into HSV + r = int(m.group("r")) + g = int(m.group("g")) + b = int(m.group("b")) + h, s, v = rgb_to_hsv(r, g, b) + + # write this out as a C array entry + name = m.group("name") + a = int(m.group("a")) + options.output.write(f'{{"{name}",{h},{s},{v},{r},{g},{b},{a}}},\n') + + options.output.write("};\n") + + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv)) diff --git a/lib/gvc.vcxproj b/lib/gvc.vcxproj index 31100ae4c..bd42ba62b 100644 --- a/lib/gvc.vcxproj +++ b/lib/gvc.vcxproj @@ -74,8 +74,7 @@ win_bison -dy -Wno-yacc common\htmlparse.y -o common\htmlparse.c python common\make_svgcolor_lib.py common\svgcolor_names common\svgcolor_lib python common\make_brewer_lib.py common\brewer_colors common\brewer_lib -type common\brewer_lib common\svgcolor_lib common\color_names | sort /L C > color_lib -awk -f $(SolutionDir)awk\colortbl.awk color_lib > common\colortbl.h +python common\make_colortbl.py common\brewer_lib common\svgcolor_lib common\color_names common\colortbl.h copy $(SolutionDir)windows\dependencies\libraries\x86\bin\expat.dll $(OutDir)expat.dll @@ -105,8 +104,7 @@ awk -f $(SolutionDir)awk\colortbl.awk color_lib > common\colortbl.h win_bison -dy -Wno-yacc common\htmlparse.y -o common\htmlparse.c python common\make_svgcolor_lib.py common\svgcolor_names common\svgcolor_lib python common\make_brewer_lib.py common\brewer_colors common\brewer_lib -type common\brewer_lib common\svgcolor_lib common\color_names | sort /L C > color_lib -awk -f $(SolutionDir)awk\colortbl.awk color_lib > common\colortbl.h +python common\make_colortbl.py common\brewer_lib common\svgcolor_lib common\color_names common\colortbl.h copy $(SolutionDir)windows\dependencies\libraries\x86\bin\expat.dll $(OutDir)expat.dll -- 2.40.0