From 13d8ee65bfb1587a50e49d9cbf3dec830f97d715 Mon Sep 17 00:00:00 2001 From: Jay Fitzgerald <34140133+ni-jfitzger@users.noreply.github.com> Date: Wed, 14 Oct 2026 10:13:05 -0500 Subject: [PATCH 01/10] Delete makefile code --- Makefile | 18 ---- build.sh | 5 -- build/Makefile | 168 ------------------------------------ build/__main__.py | 84 ------------------ build/defines.mak | 97 --------------------- build/rules.mak | 160 ---------------------------------- build/tools.mak | 40 --------- src/nidcpower/nidcpower.mak | 19 ---- src/nidigital/nidigital.mak | 17 ---- src/nidmm/nidmm.mak | 15 ---- src/nifake/nifake.mak | 19 ---- src/nifgen/nifgen.mak | 15 ---- src/nimodinst/nimodinst.mak | 23 ----- src/nirfsa/nirfsa.mak | 20 ----- src/nirfsg/nirfsg.mak | 15 ---- src/niscope/niscope.mak | 19 ---- src/nise/nise.mak | 16 ---- src/niswitch/niswitch.mak | 15 ---- src/nitclk/nitclk.mak | 25 ------ 19 files changed, 790 deletions(-) delete mode 100644 Makefile delete mode 100755 build.sh delete mode 100644 build/Makefile delete mode 100644 build/__main__.py delete mode 100644 build/defines.mak delete mode 100644 build/rules.mak delete mode 100644 build/tools.mak delete mode 100644 src/nidcpower/nidcpower.mak delete mode 100644 src/nidigital/nidigital.mak delete mode 100644 src/nidmm/nidmm.mak delete mode 100644 src/nifake/nifake.mak delete mode 100644 src/nifgen/nifgen.mak delete mode 100644 src/nimodinst/nimodinst.mak delete mode 100644 src/nirfsa/nirfsa.mak delete mode 100644 src/nirfsg/nirfsg.mak delete mode 100644 src/niscope/niscope.mak delete mode 100644 src/nise/nise.mak delete mode 100644 src/niswitch/niswitch.mak delete mode 100644 src/nitclk/nitclk.mak diff --git a/Makefile b/Makefile deleted file mode 100644 index 45a9696d28..0000000000 --- a/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -# In alphabetical order except put nifake first and nimodinst/nitclk last -# - nifake first to get the most code generator coverage -# - nimodinst last so that the version from nimodinst is used for any global versions (docs/conf.py) -ALL_DRIVERS := nifake nidcpower nidigital nidmm nifgen nirfsa nirfsg niscope niswitch nise nimodinst nitclk -DRIVERS ?= $(ALL_DRIVERS) - -ROOT_DIR := $(abspath .) -export ROOT_DIR - -# build related source files -BUILD_HELPER_DIR := $(ROOT_DIR)/build -export BUILD_HELPER_DIR - -GENERATED_DIR := $(ROOT_DIR)/generated -export GENERATED_DIR - -include $(BUILD_HELPER_DIR)/Makefile - diff --git a/build.sh b/build.sh deleted file mode 100755 index 8b526bf55a..0000000000 --- a/build.sh +++ /dev/null @@ -1,5 +0,0 @@ -# Python 3 is required for build.py - -make clean -make - diff --git a/build/Makefile b/build/Makefile deleted file mode 100644 index 0ff8e645a0..0000000000 --- a/build/Makefile +++ /dev/null @@ -1,168 +0,0 @@ -include $(BUILD_HELPER_DIR)/tools.mak - -DEFAULT_TARGETS := module doc_files -export DEFAULT_TARGETS - -GENERATED_DIR := $(ROOT_DIR)/generated -export GENERATED_DIR - -COMMAND_LOG := $(ROOT_DIR)/commands.log -export COMMAND_LOG - -DOCS_DIR := $(ROOT_DIR)/docs -export DOCS_DIR - -STATIC_DOCS_DIR := $(DOCS_DIR)/_static -export STATIC_DOCS_DIR - -ifeq (,$(PRINT)) -_hide_cmds := @ -endif - -# We only add root files if we are building the 'all' target (nothing on the command line) -ifeq (,$(MAKECMDGOALS)) -ROOT_FILES := \ - $(ROOT_DIR)/README.rst \ - -endif - -all: $(DRIVERS) $(ROOT_FILES) - -define per_driver_per_target -$1_$2: start - @echo - @echo "Making $1" - $(_hide_cmds)make --no-print-directory -s -f src/$1/$1.mak DRIVER=$1 MKDIR - $(_hide_cmds)make --no-print-directory -s -f src/$1/$1.mak DRIVER=$1 $2 -endef - -define per_driver_all -$1: start - @echo - @echo "Making $1" - $(_hide_cmds)make --no-print-directory -s -f src/$1/$1.mak DRIVER=$1 MKDIR - $(_hide_cmds)make --no-print-directory -s -f src/$1/$1.mak DRIVER=$1 -endef - -$(foreach d,$(ALL_DRIVERS),$(eval $(call per_driver_all,$(d)))) -$(foreach d,$(ALL_DRIVERS),\ - $(foreach t,$(DEFAULT_TARGETS),\ - $(eval $(call per_driver_per_target,$(d),$(t))))) - -define per_target -$1: - $(foreach d,$(DRIVERS),make --no-print-directory -f src/$(d)/$(d).mak DRIVER=$(d) $1 &&) echo -endef - -$(foreach t,$(DEFAULT_TARGETS),$(eval $(call per_target,$(t)))) - -define per_driver_installers -$1_installers: start - @echo - @echo "Making $1 installers" - $(_hide_cmds)make --no-print-directory -s -f src/$1/$1.mak DRIVER=$1 MKDIR - $(_hide_cmds)make --no-print-directory -s -f src/$1/$1.mak DRIVER=$1 installers -endef - -$(foreach d,$(ALL_DRIVERS),$(eval $(call per_driver_installers,$(d)))) -installers: $(foreach d,$(ALL_DRIVERS), $(d)_installers) - -SUPPRESS_ERROR_OUTPUT ?= 2> /dev/null - -# Keep the generated directory available for CI builds -clean: start - @echo 'Cleaning...' - -$(_hide_cmds)rm -Rf $(GENERATED_DIR)/ $(SUPPRESS_ERROR_OUTPUT) ||: - -$(_hide_cmds)find $(ROOT_DIR)/build -path '*/__pycache__/*' -exec rm {} \; $(SUPPRESS_ERROR_OUTPUT) ||: - -$(_hide_cmds)find $(ROOT_DIR)/build -name __pycache__ -exec rmdir {} \; $(SUPPRESS_ERROR_OUTPUT) ||: - -$(_hide_cmds)find $(ROOT_DIR)/build -name '*.pyc' -exec rm {} \; $(SUPPRESS_ERROR_OUTPUT) ||: - -$(_hide_cmds)rm $(foreach d,$(DRIVERS), $(ROOT_DIR)/docs/$(d)/*) $(SUPPRESS_ERROR_OUTPUT) ||: - -$(_hide_cmds)rm $(foreach d,$(DRIVERS), $(ROOT_DIR)/docs/$(d)/.readthedocs.yaml) $(SUPPRESS_ERROR_OUTPUT) ||: - -$(_hide_cmds)rm -Rf $(ROOT_DIR)/.coverage $(SUPPRESS_ERROR_OUTPUT) ||: - -$(_hide_cmds)rm -Rf $(ROOT_DIR)/README.rst $(SUPPRESS_ERROR_OUTPUT) ||: - -$(_hide_cmds)mkdir $(GENERATED_DIR) $(SUPPRESS_ERROR_OUTPUT) ||: - -start: - -@echo "# This is the list of commands that make invoked in order to build nimi-python. If" > $(COMMAND_LOG) - -@echo "# you want to reproduce the build but don't have GNU Make setup in your system, you" >> $(COMMAND_LOG) - -@echo "# can use the commands in this log file." >> $(COMMAND_LOG) - - -# From https://stackoverflow.com/questions/14760124/how-to-split-in-gnu-makefile-list-of-files-into-separate-lines -DRIVER_ALL_TARGETS_HELP := echo Drivers: $(addprefix && echo - ,$(ALL_DRIVERS)) -TARGETS_HELP := echo Targets: $(addprefix && echo - ,$(DEFAULT_TARGETS)) -PER_DRIVER_PER_TARGET := \ - $(foreach d,$(ALL_DRIVERS),\ - $(foreach t,$(DEFAULT_TARGETS),\ - $(d)_$(t))) -DRIVER_TARGETS_HELP := echo Per driver, per target: $(addprefix && echo - ,$(PER_DRIVER_PER_TARGET)) -help: - @echo 'Supported targets:' - @echo '* help' - @echo '* clean' - @echo '* print- (only top level variables)' - @echo '* printvar VAR= (per driver print variable)' - @echo '' - @echo 'Any/multiple target(s) listed below:' - @$(DRIVER_ALL_TARGETS_HELP) - @echo '' - @$(TARGETS_HELP) - @echo '' - @$(DRIVER_TARGETS_HELP) - @echo '' - -# From https://stackoverflow.com/questions/16467718/how-to-print-out-a-variable-in-makefile -print-%: ; $(info $* is $(flavor $*) variable set to [$($*)]) @true - -# Per driver variable printing -per_driver_variable_print = make --no-print-directory -f src/$1/$1.mak DRIVER=$1 print-$2 -.PHONY: -printvar: - $(_hide_cmds)$(foreach d,$(DRIVERS),$(call per_driver_variable_print,$(d),$(VAR)) && ) echo - - -PYTHON_CMD ?= python - -.PHONY: help Makefile - -# For the global files, instead of trying to keep track of which files from the driver builds matter for any target, -# we just say that if anything from any driver built, we will rebuld the global files -export DRIVER_FILE_BUILT = $(GENERATED_DIR)/driver_file_built - -GLOBAL_FILES_STARTED_FILE = $(GENERATED_DIR)/printed_global_files -$(GLOBAL_FILES_STARTED_FILE): $(DRIVER_FILE_BUILT) - @echo - @echo "Making Global Files" - @touch $(GLOBAL_FILES_STARTED_FILE) - -# Executes a command, then logs it to $(COMMAND_LOG). -# $1 is the command. -# We need our own copy that does not touch DRIVER_FILE_BUILT -define global_log_command - $1 - @echo '$1' >> $(COMMAND_LOG) -endef - -$(ROOT_DIR)/README.rst: $(GLOBAL_FILES_STARTED_FILE) - $(call trace_to_console, "Creating Root",$(notdir $@)) - $(_hide_cmds)$(call global_log_command,cat $(STATIC_DOCS_DIR)/status_project.inc \ - $(STATIC_DOCS_DIR)/about.inc \ - $(DOCS_DIR)/*/status.inc \ - $(STATIC_DOCS_DIR)/installation.inc \ - $(STATIC_DOCS_DIR)/contributing.inc \ - $(STATIC_DOCS_DIR)/nidmm_usage.inc \ - $(STATIC_DOCS_DIR)/support.inc \ - $(STATIC_DOCS_DIR)/documentation.inc \ - $(STATIC_DOCS_DIR)/license.inc > $@) - -# Any step that any driver build does that would invalidate unit testing, flake8 or generated html -# needs to delete this file. This will trigger a tox run. -TOX_RUN_DONE := $(GENERATED_DIR)/tox_run_done -export TOX_RUN_DONE - -test: $(TOX_RUN_DONE) - -$(TOX_RUN_DONE): - @echo - $(call trace_to_console, "Running tox",$@) - $(_hide_cmds)$(call make_with_tracking_file,$@,tox) diff --git a/build/__main__.py b/build/__main__.py deleted file mode 100644 index 1d83a615d7..0000000000 --- a/build/__main__.py +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/python3 - -import argparse -import logging -import os -import pprint -import sys - -# Part of this package -import generate_template -import utilities - -pp = pprint.PrettyPrinter(indent=4) - -# Setup the required arguments for this script -usage = "Codegen driver files" - -parser = argparse.ArgumentParser(description=usage, prog='build') -verbosity_group = parser.add_argument_group("Verbosity, Logging & Debugging") -verbosity_group.add_argument( - "-v", "--verbose", - action="count", dest="verbose", default=0, - help="Verbose output") -verbosity_group.add_argument( - "--test", - action="store_true", dest="test", default=False, - help="Run doctests and quit") -verbosity_group.add_argument( - "--log-file", - action="store", dest="logfile", default=None, - help="Send logging to listed file instead of stdout") -build_group = parser.add_argument_group("Build") -build_group.add_argument( - "--metadata", - action='append', dest='metadata', default=[], - help='Absolute or relative path to metadata package. Multiple allowed. Will build in order added to command line.') -build_group.add_argument( - "--include-grpc-support", - action='store_true', dest='include_grpc_support', default=[], - help='Include gRPC support in the generated files.') -utility_group = parser.add_argument_group("Utility") -utility_group.add_argument( - "--template", - action="store", dest="template", default=None, required=True, - help="Mako template to use. Can only be used if no actions.") -utility_group.add_argument( - "--dest-dir", - action="store", dest="dest_dir", default=None, required=False, - help="Output folder.") -utility_group.add_argument( - "--dest-file", - action="store", dest="dest_file", default=None, required=False, - help="Output file name. Optional. If not set, file will be named based on mako template name.") -args = parser.parse_args() - -if args.verbose > 1: - utilities.configure_logging(logging.DEBUG, args.logfile) -elif args.verbose == 1: - utilities.configure_logging(logging.INFO, args.logfile) -else: - utilities.configure_logging(logging.WARNING, args.logfile) - -logging.info(pp.pformat(args)) - -for m in args.metadata: - if not os.path.isabs(m): - m = os.path.normpath(os.path.join(os.getcwd(), m)) - metadata = utilities.load_build(m) - - if args.dest_dir is None: - logging.error('--dest-dir is required when using --template') - sys.exit(1) - - template_params = {} - template_params['metadata'] = metadata - template_params['include_grpc_support'] = args.include_grpc_support - - logging.debug(pp.pformat(template_params)) - - file_name = args.dest_file - if file_name is None: - file_name = os.path.basename(args.template).replace('.mako', '') - dest_file = os.path.join(args.dest_dir, file_name) - generate_template.generate_template(args.template, template_params, dest_file) diff --git a/build/defines.mak b/build/defines.mak deleted file mode 100644 index be6c10fe90..0000000000 --- a/build/defines.mak +++ /dev/null @@ -1,97 +0,0 @@ -OUTPUT_DIR := $(GENERATED_DIR)/$(DRIVER) -LOG_DIR := $(OUTPUT_DIR)/log -MODULE_DIR := $(OUTPUT_DIR)/$(DRIVER) -UNIT_TEST_DIR := $(MODULE_DIR)/unit_tests -TEMPLATE_DIR := $(BUILD_HELPER_DIR)/templates -TOX_INI := $(OUTPUT_DIR)/tox.ini - -DRIVER_DIR := $(ROOT_DIR)/src/$(DRIVER) -METADATA_DIR := $(DRIVER_DIR)/metadata -METADATA_FILES := $(wildcard $(METADATA_DIR)/*.py) -SHARED_PROTOS_DIR := $(ROOT_DIR)/src/shared_protos -NI_APIS_PROTOS_DIR := $(ROOT_DIR)/third_party/ni-apis/ni/grpcdevice/v1 -PROTO_DIRS := $(METADATA_DIR) $(SHARED_PROTOS_DIR) $(NI_APIS_PROTOS_DIR) -PROTO_FILE ?= $(METADATA_DIR)/$(DRIVER).proto - -BUILD_HELPER_SCRIPTS := $(wildcard $(BUILD_HELPER_DIR)/*.py $(BUILD_HELPER_DIR)/helper/*.py) - -DRIVER_DOCS_DIR := $(DOCS_DIR)/$(DRIVER) - -MKDIRECTORIES += \ - $(DRIVER_DOCS_DIR) \ - $(OUTPUT_DIR) \ - $(MODULE_DIR) \ - $(UNIT_TEST_DIR) \ - $(LOG_DIR) \ - -VPATH = $(TEMPLATE_DIR) - -true := T -false := -GRPC_SUPPORTED := $(if $(wildcard $(PROTO_FILE)),$(true)) - -PYTHON_CMD ?= python -GRPC_SUPPORT_PARAM := $(if $(GRPC_SUPPORTED),--include-grpc-support) -define GENERATE_SCRIPT -$(PYTHON_CMD) -m build --template $1 --dest-dir $2 --metadata $3 $(if $4,--dest-file $4) $(if $(PRINT),-v,) $(GRPC_SUPPORT_PARAM) -endef - -ifeq (,$(PRINT)) -_hide_cmds := @ -LOG_OUTPUT := > -else -LOG_OUTPUT := | tee -endif - -TARGETS := $(filter-out run_unit_tests,$(DEFAULT_TARGETS)) - -.PHONY: -all: $(TARGETS) - -DEFAULT_PY_FILES_TO_GENERATE := \ - _attributes.py \ - enums.py \ - _library.py \ - _library_interpreter.py \ - _library_singleton.py \ - session.py \ - errors.py \ - unit_tests/_mock_helper.py \ - unit_tests/_matchers.py \ - __init__.py \ - _converters.py \ - VERSION \ - $(if $(GRPC_SUPPORTED), \ - _grpc_stub_interpreter.py \ - grpc_session_options.py \ - $(basename $(notdir $(PROTO_FILE)))_pb2.py \ - $(basename $(notdir $(PROTO_FILE)))_pb2_grpc.py \ - nidevice_pb2.py \ - nidevice_pb2_grpc.py \ - ) \ - -DEFAULT_PY_FILES_TO_COPY := \ - _visatype.py \ - -DEFAULT_RST_FILES_TO_GENERATE := \ - about_$(DRIVER).inc \ - index.rst \ - $(DRIVER).rst \ - enums.rst \ - examples.rst \ - installation.inc \ - status.inc \ - class.rst \ - toc.inc \ - errors.rst \ - rep_caps.rst \ - $(if $(GRPC_SUPPORTED), \ - grpc_session_options.rst \ - ) \ - -DEFAULT_SPHINX_CONF_PY := $(DRIVER_DOCS_DIR)/conf.py -DEFAULT_READTHEDOCS_CONFIG := $(DRIVER_DOCS_DIR)/.readthedocs.yaml - -# Files for tracking parts of the build -SDIST_WHEEL_BUILD_DONE := $(LOG_DIR)/sdist_wheel_build_done -GENERATED_FILES_COPY_DONE := $(LOG_DIR)/generated_files_copy_done diff --git a/build/rules.mak b/build/rules.mak deleted file mode 100644 index 03a9bcbf93..0000000000 --- a/build/rules.mak +++ /dev/null @@ -1,160 +0,0 @@ - -include $(BUILD_HELPER_DIR)/tools.mak - -README := $(OUTPUT_DIR)/README.rst -SETUP := $(OUTPUT_DIR)/setup.py -TOX_INI := $(OUTPUT_DIR)/tox-system_tests.ini - -MODULE_FILES := \ - $(addprefix $(MODULE_DIR)/,$(MODULE_FILES_TO_GENERATE)) \ - $(addprefix $(MODULE_DIR)/,$(MODULE_FILES_TO_COPY)) \ - $(addprefix $(MODULE_DIR)/,$(CUSTOM_TYPES_TO_COPY)) \ - $(README) \ - $(SETUP) \ - $(TOX_INI) \ - -RST_FILES := \ - $(addprefix $(DRIVER_DOCS_DIR)/,$(RST_FILES_TO_GENERATE)) \ - -EXAMPLE_FILES := $(if $(wildcard src/$(DRIVER)/examples/*),$(shell find src/$(DRIVER)/examples/* -type f -print),) - -# If there are any examples, we will need to build the examples zip file for this driver -ifneq (,$(EXAMPLE_FILES)) - -EXAMPLES_DIR := $(GENERATED_DIR)/examples -MKDIRECTORIES += $(EXAMPLES_DIR) -DRIVER_EXAMPLES_ZIP_FILE := $(EXAMPLES_DIR)/$(DRIVER)_examples.zip -MODULE_FILES += $(DRIVER_EXAMPLES_ZIP_FILE) - -endif # ifneq (,$(EXAMPLE_FILES)) - -MKDIR: $(MKDIRECTORIES) - -define mkdir_rule -$1: - $(call trace_to_console, "Making dir",$1) - $(_hide_cmds)$(call log_command,mkdir -p $1) -endef -$(foreach d,$(MKDIRECTORIES),$(eval $(call mkdir_rule,$(d)))) - -# We set up some additional dependencies for specific files -# examples.rst needs to use find since there may be folders of files and it needs to be recursive. wildcard is not recursive -$(MODULE_DIR)/session.py: $(wildcard $(TEMPLATE_DIR)/session.py/*.mako) $(wildcard $(DRIVER_DIR)/templates/session.py/*.mako) -$(MODULE_DIR)/_grpc_stub_interpreter.py: $(wildcard $(TEMPLATE_DIR)/_grpc_stub_interpreter.py/*.mako) $(wildcard $(DRIVER_DIR)/templates/_grpc_stub_interpreter.py/*.mako) -$(MODULE_DIR)/_library_interpreter.py: $(wildcard $(TEMPLATE_DIR)/_library_interpreter.py/*.mako) $(wildcard $(DRIVER_DIR)/templates/_library_interpreter.py/*.mako) -$(DRIVER_DOCS_DIR)/class.rst: $(wildcard $(TEMPLATE_DIR)/functions.rst/*.mako) $(wildcard $(DRIVER_DIR)/templates/functions.rst/*.mako) -$(DRIVER_DOCS_DIR)/examples.rst: $(EXAMPLE_FILES) $(MODULE_DIR)/VERSION - -$(MODULE_DIR)/%.py: %.py.mako $(BUILD_HELPER_SCRIPTS) $(METADATA_FILES) - $(call trace_to_console, "Generating",$@) - $(_hide_cmds)$(call log_command,$(call GENERATE_SCRIPT, $<, $(dir $@), $(METADATA_DIR))) - -$(MODULE_DIR)/unit_tests/%.py: %.py.mako $(BUILD_HELPER_SCRIPTS) $(METADATA_FILES) - $(call trace_to_console, "Generating",$@) - $(_hide_cmds)$(call log_command,$(call GENERATE_SCRIPT, $<, $(dir $@), $(METADATA_DIR))) - -$(MODULE_DIR)/%: %.mako $(BUILD_HELPER_SCRIPTS) $(METADATA_FILES) - $(call trace_to_console, "Generating",$@) - $(_hide_cmds)$(call log_command,$(call GENERATE_SCRIPT, $<, $(dir $@), $(METADATA_DIR))) - -$(MODULE_DIR)/%_pb2.py: %.proto - $(call trace_to_console, "Generating",$@ and $(notdir $*)_pb2_grpc.py) - $(_hide_cmds)$(call log_command,python -m grpc_tools.protoc $(addprefix -I=,$(PROTO_DIRS)) --python_out=$(MODULE_DIR) --grpc_python_out=$(MODULE_DIR) $*.proto) - $(_hide_cmds)$(call log_command,sed -i 's/^import nidevice_pb2/from . import nidevice_pb2/' $(MODULE_DIR)/$*_pb2*.py) - $(_hide_cmds)$(call log_command,sed -i 's/^import $(notdir $*)_pb2/from . import $(notdir $*)_pb2/' $(MODULE_DIR)/$*_pb2*.py) - -vpath %.proto $(PROTO_DIRS) - -$(MODULE_DIR)/%_pb2_grpc.py: $(MODULE_DIR)/%_pb2.py - -$(MODULE_DIR)/%.py: %.py - $(call trace_to_console, "Copying",$@) - $(_hide_cmds)cp $< $@ - -$(MODULE_DIR)/%.py: $(DRIVER_DIR)/custom_types/%.py - $(call trace_to_console, "Copying",$@) - $(_hide_cmds)cp $< $@ - -$(DRIVER_DOCS_DIR)/%.rst: %.rst.mako $(BUILD_HELPER_SCRIPTS) $(METADATA_FILES) - $(call trace_to_console, "Generating",$@) - $(_hide_cmds)$(call log_command,$(call GENERATE_SCRIPT, $<, $(dir $@), $(METADATA_DIR))) - -$(DRIVER_DOCS_DIR)/$(DRIVER).rst: driver.rst.mako $(BUILD_HELPER_SCRIPTS) $(METADATA_FILES) - $(call trace_to_console, "Generating",$@) - $(_hide_cmds)$(call log_command,$(call GENERATE_SCRIPT, $<, $(dir $@), $(METADATA_DIR), $(notdir $@))) - -$(DRIVER_DOCS_DIR)/%.inc: %.inc.mako $(BUILD_HELPER_SCRIPTS) $(METADATA_FILES) - $(call trace_to_console, "Generating",$@) - $(_hide_cmds)$(call log_command,$(call GENERATE_SCRIPT, $<, $(dir $@), $(METADATA_DIR))) - -$(DRIVER_DOCS_DIR)/about_$(DRIVER).inc: about_driver.inc.mako $(BUILD_HELPER_SCRIPTS) $(METADATA_FILES) - $(call trace_to_console, "Generating",$@) - $(_hide_cmds)$(call log_command,$(call GENERATE_SCRIPT, $<, $(dir $@), $(METADATA_DIR), $(notdir $@))) - -$(SPHINX_CONF_PY): $(TEMPLATE_DIR)/conf.py.mako $(BUILD_HELPER_SCRIPTS) $(METADATA_FILES) - $(call trace_to_console, "Generating",$@) - $(_hide_cmds)$(call log_command,$(call GENERATE_SCRIPT, $<, $(dir $@), $(METADATA_DIR))) - -$(READTHEDOCS_CONFIG): $(TEMPLATE_DIR)/.readthedocs.yaml.mako $(BUILD_HELPER_SCRIPTS) $(METADATA_FILES) - $(call trace_to_console, "Generating",$@) - $(_hide_cmds)$(call log_command,$(call GENERATE_SCRIPT, $<, $(dir $@), $(METADATA_DIR))) - -$(DRIVER_EXAMPLES_ZIP_FILE): $(EXAMPLE_FILES) - $(call trace_to_console, "Zipping",$@) - $(_hide_cmds)$(call log_command,cd src/$(DRIVER)/examples && zip -u -r -9 $@ * || ([ $$? -eq 12 ] && exit 0) || exit) - -UNIT_TEST_FILES_TO_COPY := $(wildcard $(DRIVER_DIR)/unit_tests/*.py) -UNIT_TEST_FILES := $(addprefix $(UNIT_TEST_DIR)/,$(notdir $(UNIT_TEST_FILES_TO_COPY))) - -$(UNIT_TEST_DIR)/%.py: $(DRIVER_DIR)/unit_tests/%.py - $(call trace_to_console, "Copying",$@) - $(_hide_cmds)$(call log_command,cp $< $@) - -clean: - -.PHONY: module doc_files sdist wheel installers -module: $(MODULE_FILES) $(UNIT_TEST_FILES) -doc_files: $(RST_FILES) $(SPHINX_CONF_PY) $(READTHEDOCS_CONFIG) -installers: sdist wheel - -$(UNIT_TEST_FILES): $(MODULE_FILES) - -$(SETUP): $(TEMPLATE_DIR)/setup.py.mako $(METADATA_FILES) - $(call trace_to_console, "Generating",$@) - $(_hide_cmds)$(call log_command,$(call GENERATE_SCRIPT, $<, $(dir $@), $(METADATA_DIR))) - -$(TOX_INI): $(TEMPLATE_DIR)/tox-system_tests.ini.mako $(METADATA_FILES) - $(call trace_to_console, "Generating",$@) - $(_hide_cmds)$(call log_command,$(call GENERATE_SCRIPT, $<, $(dir $@), $(METADATA_DIR))) - -sdist wheel: $(SDIST_WHEEL_BUILD_DONE) - -$(SDIST_WHEEL_BUILD_DONE): # codegen should have already run or just use what is in git - $(call trace_to_console, "Creating sdist and wheel",$(OUTPUT_DIR)/dist) - $(_hide_cmds)$(call log_command_no_tracking,cd $(OUTPUT_DIR) && $(PYTHON_CMD) -m build $(LOG_OUTPUT) $(LOG_DIR)/sdist_wheel.log) - $(_hide_cmds)$(call log_command_no_tracking,touch $@) - -# If we are building nifake, we just need a placeholder file for inclusion into the wheel that will never be used. We can't build the actual readme since not all the files are created -ifeq (nifake,$(DRIVER)) -$(README): - $(call trace_to_console, "Copying",$@) - $(_hide_cmds)$(call log_command,cp $(ROOT_DIR)/LICENSE $@) - -else -# We piece together the readme files instead of relying on the rst include directive because we need these files to be standalone and not require any additional files that are in specific locations. -$(README): $(RST_FILES) $(wildcard $(STATIC_DOCS_DIR)/*) - $(call trace_to_console, "Creating",$@) - $(_hide_cmds)$(call log_command,cat $(STATIC_DOCS_DIR)/status_project.inc \ - $(DRIVER_DOCS_DIR)/about_$(DRIVER).inc \ - $(DRIVER_DOCS_DIR)/status.inc \ - $(DRIVER_DOCS_DIR)/installation.inc \ - $(STATIC_DOCS_DIR)/contributing.inc \ - $(STATIC_DOCS_DIR)/$(DRIVER)_usage.inc \ - $(STATIC_DOCS_DIR)/support.inc \ - $(STATIC_DOCS_DIR)/$(DRIVER)_documentation.inc \ - $(STATIC_DOCS_DIR)/license.inc > $@) - -endif - -# From https://stackoverflow.com/questions/16467718/how-to-print-out-a-variable-in-makefile -print-%: ; $(info $(DRIVER): $* is $(flavor $*) variable set to [$($*)]) @true diff --git a/build/tools.mak b/build/tools.mak deleted file mode 100644 index 8471717a4f..0000000000 --- a/build/tools.mak +++ /dev/null @@ -1,40 +0,0 @@ -# Useful functions - -CURRENT_DIR := $(shell pwd) - -# Traces to console, nicely formatted. -# $1 is the Action, for example: "Generating" -# $2 is a Path. -# Action will be padded on the left so colons align, for readability. -# Path will be turned from absolute to relative, for readability. -define trace_to_console - @echo "$(shell printf '%15s' $1): $(subst $(CURRENT_DIR)/,,$2)" -endef - -# Executes a command, logs it to $(COMMAND_LOG), updates tracking file to show module file generated. -# $1 is the command. -define log_command - $1 - @echo '$1' >> $(COMMAND_LOG) - @touch $(DRIVER_FILE_BUILT) -endef - -# Executes a command, then logs it to $(COMMAND_LOG). -# $1 is the command. -define log_command_no_tracking - $1 - @echo '$1' >> $(COMMAND_LOG) -endef - -# Helper function for running a command with a tracking file -# created/updated upon completion -# $1 is tracking file path -# $2 is command to run -define make_with_tracking_file - $(_hide_cmds)$(call log_command,touch $1) - $(_hide_cmds)$(call log_command,rm $1) - $(_hide_cmds)$(call log_command,$2) - $(_hide_cmds)$(call log_command,touch $1) -endef - - diff --git a/src/nidcpower/nidcpower.mak b/src/nidcpower/nidcpower.mak deleted file mode 100644 index 11f41d27d0..0000000000 --- a/src/nidcpower/nidcpower.mak +++ /dev/null @@ -1,19 +0,0 @@ - - -include $(BUILD_HELPER_DIR)/defines.mak - -MODULE_FILES_TO_GENERATE := $(DEFAULT_PY_FILES_TO_GENERATE) - -MODULE_FILES_TO_COPY := $(DEFAULT_PY_FILES_TO_COPY) - -RST_FILES_TO_GENERATE := $(DEFAULT_RST_FILES_TO_GENERATE) - -SPHINX_CONF_PY := $(DEFAULT_SPHINX_CONF_PY) -READTHEDOCS_CONFIG := $(DEFAULT_READTHEDOCS_CONFIG) - -CUSTOM_TYPES_TO_COPY += \ - lcr_load_compensation_spot.py \ - lcr_measurement.py \ - -include $(BUILD_HELPER_DIR)/rules.mak - diff --git a/src/nidigital/nidigital.mak b/src/nidigital/nidigital.mak deleted file mode 100644 index ed238f1f3b..0000000000 --- a/src/nidigital/nidigital.mak +++ /dev/null @@ -1,17 +0,0 @@ -PROTO_FILE = $(METADATA_DIR)/nidigitalpattern.proto - -include $(BUILD_HELPER_DIR)/defines.mak - -MODULE_FILES_TO_GENERATE := $(DEFAULT_PY_FILES_TO_GENERATE) - -MODULE_FILES_TO_COPY := $(DEFAULT_PY_FILES_TO_COPY) - -RST_FILES_TO_GENERATE := $(DEFAULT_RST_FILES_TO_GENERATE) - -SPHINX_CONF_PY := $(DEFAULT_SPHINX_CONF_PY) -READTHEDOCS_CONFIG := $(DEFAULT_READTHEDOCS_CONFIG) - -CUSTOM_TYPES_TO_COPY += \ - history_ram_cycle_information.py \ - -include $(BUILD_HELPER_DIR)/rules.mak diff --git a/src/nidmm/nidmm.mak b/src/nidmm/nidmm.mak deleted file mode 100644 index 874324a800..0000000000 --- a/src/nidmm/nidmm.mak +++ /dev/null @@ -1,15 +0,0 @@ - - -include $(BUILD_HELPER_DIR)/defines.mak - -MODULE_FILES_TO_GENERATE := $(DEFAULT_PY_FILES_TO_GENERATE) - -MODULE_FILES_TO_COPY := $(DEFAULT_PY_FILES_TO_COPY) - -RST_FILES_TO_GENERATE := $(filter-out rep_caps.rst,$(DEFAULT_RST_FILES_TO_GENERATE)) - -SPHINX_CONF_PY := $(DEFAULT_SPHINX_CONF_PY) -READTHEDOCS_CONFIG := $(DEFAULT_READTHEDOCS_CONFIG) - -include $(BUILD_HELPER_DIR)/rules.mak - diff --git a/src/nifake/nifake.mak b/src/nifake/nifake.mak deleted file mode 100644 index 85386b617a..0000000000 --- a/src/nifake/nifake.mak +++ /dev/null @@ -1,19 +0,0 @@ - - -include $(BUILD_HELPER_DIR)/defines.mak - -MODULE_FILES_TO_GENERATE := $(DEFAULT_PY_FILES_TO_GENERATE) _complextype.py - -MODULE_FILES_TO_COPY := $(DEFAULT_PY_FILES_TO_COPY) - -# We are not building any nifake documentation -# RST_FILES_TO_GENERATE := $(DEFAULT_RST_FILES_TO_GENERATE) -# SPHINX_CONF_PY := $(DEFAULT_SPHINX_CONF_PY) - -CUSTOM_TYPES_TO_COPY += \ - custom_struct.py \ - custom_struct_typedef.py \ - custom_struct_nested_typedef.py \ - -include $(BUILD_HELPER_DIR)/rules.mak - diff --git a/src/nifgen/nifgen.mak b/src/nifgen/nifgen.mak deleted file mode 100644 index 010defb466..0000000000 --- a/src/nifgen/nifgen.mak +++ /dev/null @@ -1,15 +0,0 @@ - - -include $(BUILD_HELPER_DIR)/defines.mak - -MODULE_FILES_TO_GENERATE := $(DEFAULT_PY_FILES_TO_GENERATE) - -MODULE_FILES_TO_COPY := $(DEFAULT_PY_FILES_TO_COPY) - -RST_FILES_TO_GENERATE := $(DEFAULT_RST_FILES_TO_GENERATE) - -SPHINX_CONF_PY := $(DEFAULT_SPHINX_CONF_PY) -READTHEDOCS_CONFIG := $(DEFAULT_READTHEDOCS_CONFIG) - -include $(BUILD_HELPER_DIR)/rules.mak - diff --git a/src/nimodinst/nimodinst.mak b/src/nimodinst/nimodinst.mak deleted file mode 100644 index 6d99daa012..0000000000 --- a/src/nimodinst/nimodinst.mak +++ /dev/null @@ -1,23 +0,0 @@ -include $(BUILD_HELPER_DIR)/defines.mak -include $(BUILD_HELPER_DIR)/tools.mak - -# We want everything but enums.py -MODULE_FILES_TO_GENERATE := $(filter-out enums.py _attributes.py,$(DEFAULT_PY_FILES_TO_GENERATE)) - -MODULE_FILES_TO_COPY := $(DEFAULT_PY_FILES_TO_COPY) - -RST_FILES_TO_GENERATE := $(filter-out rep_caps.rst enums.rst,$(DEFAULT_RST_FILES_TO_GENERATE)) - -SPHINX_CONF_PY := $(DEFAULT_SPHINX_CONF_PY) -READTHEDOCS_CONFIG := $(DEFAULT_READTHEDOCS_CONFIG) - -include $(BUILD_HELPER_DIR)/rules.mak - -# We need to override the default rule for generating session since we have -# a specialized copy for ModInst -$(MODULE_DIR)/session.py: $(DRIVER_DIR)/templates/session.py.mako $(BUILD_HELPER_SCRIPTS) $(METADATA_FILES) - $(call trace_to_console, "Generating",$@) - $(_hide_cmds)$(call GENERATE_SCRIPT, $<, $(MODULE_DIR), $(METADATA_DIR)) -# Need to signal the top level makefile to run tests again - $(_hide_cmds)$(call trigger_unit_tests) - diff --git a/src/nirfsa/nirfsa.mak b/src/nirfsa/nirfsa.mak deleted file mode 100644 index d1e78df16f..0000000000 --- a/src/nirfsa/nirfsa.mak +++ /dev/null @@ -1,20 +0,0 @@ - - -include $(BUILD_HELPER_DIR)/defines.mak - -MODULE_FILES_TO_GENERATE := $(DEFAULT_PY_FILES_TO_GENERATE) _complextype.py - -MODULE_FILES_TO_COPY := $(DEFAULT_PY_FILES_TO_COPY) - -RST_FILES_TO_GENERATE := $(DEFAULT_RST_FILES_TO_GENERATE) - -SPHINX_CONF_PY := $(DEFAULT_SPHINX_CONF_PY) -READTHEDOCS_CONFIG := $(DEFAULT_READTHEDOCS_CONFIG) - -CUSTOM_TYPES_TO_COPY += \ - coefficient_info_type.py \ - waveform_info.py \ - spectrum_info_type.py \ - -include $(BUILD_HELPER_DIR)/rules.mak - diff --git a/src/nirfsg/nirfsg.mak b/src/nirfsg/nirfsg.mak deleted file mode 100644 index 3570d3638f..0000000000 --- a/src/nirfsg/nirfsg.mak +++ /dev/null @@ -1,15 +0,0 @@ - - -include $(BUILD_HELPER_DIR)/defines.mak - -MODULE_FILES_TO_GENERATE := $(DEFAULT_PY_FILES_TO_GENERATE) _complextype.py - -MODULE_FILES_TO_COPY := $(DEFAULT_PY_FILES_TO_COPY) - -RST_FILES_TO_GENERATE := $(DEFAULT_RST_FILES_TO_GENERATE) - -SPHINX_CONF_PY := $(DEFAULT_SPHINX_CONF_PY) -READTHEDOCS_CONFIG := $(DEFAULT_READTHEDOCS_CONFIG) - -include $(BUILD_HELPER_DIR)/rules.mak - diff --git a/src/niscope/niscope.mak b/src/niscope/niscope.mak deleted file mode 100644 index 2fb2b94cc7..0000000000 --- a/src/niscope/niscope.mak +++ /dev/null @@ -1,19 +0,0 @@ - - -include $(BUILD_HELPER_DIR)/defines.mak - -MODULE_FILES_TO_GENERATE := $(DEFAULT_PY_FILES_TO_GENERATE) - -MODULE_FILES_TO_COPY := $(DEFAULT_PY_FILES_TO_COPY) - -RST_FILES_TO_GENERATE := $(DEFAULT_RST_FILES_TO_GENERATE) - -SPHINX_CONF_PY := $(DEFAULT_SPHINX_CONF_PY) -READTHEDOCS_CONFIG := $(DEFAULT_READTHEDOCS_CONFIG) - -CUSTOM_TYPES_TO_COPY += \ - waveform_info.py \ - measurement_stats.py \ - -include $(BUILD_HELPER_DIR)/rules.mak - diff --git a/src/nise/nise.mak b/src/nise/nise.mak deleted file mode 100644 index 45a9a6b5fd..0000000000 --- a/src/nise/nise.mak +++ /dev/null @@ -1,16 +0,0 @@ -include $(BUILD_HELPER_DIR)/defines.mak -include $(BUILD_HELPER_DIR)/tools.mak - -# We want everything but _attributes.py -MODULE_FILES_TO_GENERATE := $(filter-out _attributes.py,$(DEFAULT_PY_FILES_TO_GENERATE)) - -MODULE_FILES_TO_COPY := $(DEFAULT_PY_FILES_TO_COPY) - -RST_FILES_TO_GENERATE := $(filter-out rep_caps.rst,$(DEFAULT_RST_FILES_TO_GENERATE)) - -SPHINX_CONF_PY := $(DEFAULT_SPHINX_CONF_PY) -READTHEDOCS_CONFIG := $(DEFAULT_READTHEDOCS_CONFIG) - -include $(BUILD_HELPER_DIR)/rules.mak - - diff --git a/src/niswitch/niswitch.mak b/src/niswitch/niswitch.mak deleted file mode 100644 index 010defb466..0000000000 --- a/src/niswitch/niswitch.mak +++ /dev/null @@ -1,15 +0,0 @@ - - -include $(BUILD_HELPER_DIR)/defines.mak - -MODULE_FILES_TO_GENERATE := $(DEFAULT_PY_FILES_TO_GENERATE) - -MODULE_FILES_TO_COPY := $(DEFAULT_PY_FILES_TO_COPY) - -RST_FILES_TO_GENERATE := $(DEFAULT_RST_FILES_TO_GENERATE) - -SPHINX_CONF_PY := $(DEFAULT_SPHINX_CONF_PY) -READTHEDOCS_CONFIG := $(DEFAULT_READTHEDOCS_CONFIG) - -include $(BUILD_HELPER_DIR)/rules.mak - diff --git a/src/nitclk/nitclk.mak b/src/nitclk/nitclk.mak deleted file mode 100644 index 139b836058..0000000000 --- a/src/nitclk/nitclk.mak +++ /dev/null @@ -1,25 +0,0 @@ - - -include $(BUILD_HELPER_DIR)/defines.mak - -MODULE_FILES_TO_GENERATE := $(filter-out enums.py,$(DEFAULT_PY_FILES_TO_GENERATE)) - -MODULE_FILES_TO_COPY := $(DEFAULT_PY_FILES_TO_COPY) - -RST_FILES_TO_GENERATE := $(filter-out rep_caps.rst enums.rst,$(DEFAULT_RST_FILES_TO_GENERATE)) - -SPHINX_CONF_PY := $(DEFAULT_SPHINX_CONF_PY) -READTHEDOCS_CONFIG := $(DEFAULT_READTHEDOCS_CONFIG) - -include $(BUILD_HELPER_DIR)/rules.mak - -# We need to override the default rule for generating session since we have -# a specialized copy for TClk -$(MODULE_DIR)/session.py: $(DRIVER_DIR)/templates/session.py.mako $(BUILD_HELPER_SCRIPTS) $(METADATA_FILES) - $(call trace_to_console, "Generating",$@) - $(_hide_cmds)$(call GENERATE_SCRIPT, $<, $(MODULE_DIR), $(METADATA_DIR)) - -$(DRIVER_DOCS_DIR)/class.rst: $(DRIVER_DIR)/templates/class.rst.mako $(BUILD_HELPER_SCRIPTS) $(METADATA_FILES) - $(call trace_to_console, "Generating",$@) - $(_hide_cmds)$(call GENERATE_SCRIPT, $<, $(DRIVER_DOCS_DIR), $(METADATA_DIR)) - From 1482ec1526752a40b560b0df1e39e41274a57c42 Mon Sep 17 00:00:00 2001 From: Jay Fitzgerald <34140133+ni-jfitzger@users.noreply.github.com> Date: Wed, 14 Oct 2026 10:18:23 -0500 Subject: [PATCH 02/10] Add makefile replacement code and unit tests --- .gitattributes | 2 - .gitignore | 3 - CONTRIBUTING.md | 11 +- build/codegen.py | 446 +++++++++++++++++++++++++++++++ build/unit_tests/test_codegen.py | 198 ++++++++++++++ tox.ini | 11 +- 6 files changed, 658 insertions(+), 13 deletions(-) create mode 100644 build/codegen.py create mode 100644 build/unit_tests/test_codegen.py diff --git a/.gitattributes b/.gitattributes index d3559268a6..8c4e05e78a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -8,8 +8,6 @@ *.txt text=set eol=lf *.yml text=set eol=lf VERSION text=set eol=lf -*.mak text=set eol=lf -Makefile text=set eol=lf License text=set eol=lf *.rc text=set eol=lf *.mako text=set eol=lf diff --git a/.gitignore b/.gitignore index 12899a7278..e36cd9ef22 100644 --- a/.gitignore +++ b/.gitignore @@ -16,8 +16,6 @@ generated/*/build/ generated/*/dist/ generated/*/log/ generated/*/*.egg-info/ -generated/driver_file_built -generated/printed_global_files generated/junit/ generated/*/coverage.xml @@ -33,7 +31,6 @@ nirfsaunittest.xml niscopeunittest.xml nitclkunittest.xml codegen.xml -commands.log .vscode/launch.json .vscode/settings.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b793106838..a7245f4c46 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,8 +23,6 @@ In order to have the ability to build and run the tests you will need a few thin - Install and enable [Windows Subsystem for Linux](https://msdn.microsoft.com/en-us/commandline/wsl/install_guide) - Install the following in **Windows Subsystem for Linux**: - - GNU Make: ``sudo apt-get install make`` - - zip: ``sudo apt-get install zip`` - 64-bit [Python](https://www.python.org/downloads/) -- Use the version that `build_test` uses. See `envlist` definition in [tox.ini](./tox.ini) @@ -78,6 +76,15 @@ Building **[`nimi-python`](https://github.com/ni/nimi-python)** tox -e clean +1. To regenerate only some drivers, pass arguments to the code generator through `tox`. For example: + + tox -e py312-codegen -- --driver nidmm --driver nitclk + + `tox` isn't required: with the `codegen` dependencies installed (see `deps` in [tox.ini](./tox.ini)), you can run the + code generator directly with `python -m build.codegen generate [--driver DRIVER ...]`. + It also has `clean` and `installers` commands; use `--help` for details. + The code generator always regenerates every file it is asked to. + Updating submodules ------------------- diff --git a/build/codegen.py b/build/codegen.py new file mode 100644 index 0000000000..8cb2a02735 --- /dev/null +++ b/build/codegen.py @@ -0,0 +1,446 @@ +"""Generate the driver bindings, documentation and installers. + +Run from anywhere with ``python -m build.codegen {generate,clean,installers}``. Everything is +regenerated on every run; no attempt is made to skip work that is already up to date. +""" + +import argparse +from dataclasses import dataclass +import logging +import os +from pathlib import Path +import re +import shutil +import subprocess +import sys +import zipfile + +from build import configure_logging +from build import generate_template +from build.utilities import load_build + +ROOT_DIR = Path(__file__).resolve().parent.parent +BUILD_DIR = ROOT_DIR / 'build' +TEMPLATE_DIR = BUILD_DIR / 'templates' +GENERATED_DIR = ROOT_DIR / 'generated' +DOCS_DIR = ROOT_DIR / 'docs' +STATIC_DOCS_DIR = DOCS_DIR / '_static' +SRC_DIR = ROOT_DIR / 'src' +SHARED_PROTOS_DIR = SRC_DIR / 'shared_protos' +NI_APIS_PROTOS_DIR = ROOT_DIR / 'third_party' / 'ni-apis' / 'ni' / 'grpcdevice' / 'v1' + +DEFAULT_PY_FILES_TO_GENERATE = ( + '_attributes.py', + 'enums.py', + '_library.py', + '_library_interpreter.py', + '_library_singleton.py', + 'session.py', + 'errors.py', + 'unit_tests/_mock_helper.py', + 'unit_tests/_matchers.py', + '__init__.py', + '_converters.py', + 'VERSION', +) + +GRPC_PY_FILES_TO_GENERATE = ( + '_grpc_stub_interpreter.py', + 'grpc_session_options.py', +) + +DEFAULT_PY_FILES_TO_COPY = ( + '_visatype.py', +) + +# Documentation files that are generated for every driver, as (file name, template name). +# {driver} is replaced by the driver name. +DEFAULT_DOC_FILES_TO_GENERATE = ( + ('about_{driver}.inc', 'about_driver.inc.mako'), + ('index.rst', 'index.rst.mako'), + ('{driver}.rst', 'driver.rst.mako'), + ('enums.rst', 'enums.rst.mako'), + ('examples.rst', 'examples.rst.mako'), + ('installation.inc', 'installation.inc.mako'), + ('status.inc', 'status.inc.mako'), + ('class.rst', 'class.rst.mako'), + ('toc.inc', 'toc.inc.mako'), + ('errors.rst', 'errors.rst.mako'), + ('rep_caps.rst', 'rep_caps.rst.mako'), +) + +GRPC_DOC_FILES_TO_GENERATE = ( + ('grpc_session_options.rst', 'grpc_session_options.rst.mako'), +) + +SPHINX_FILES_TO_GENERATE = ( + ('conf.py', 'conf.py.mako'), + ('.readthedocs.yaml', '.readthedocs.yaml.mako'), +) + + +@dataclass(frozen=True) +class Driver: + """How a driver differs from the default set of generated files.""" + name: str + extra_py_files: tuple = () + omit_py_files: tuple = () + omit_doc_files: tuple = () + custom_types: tuple = () + has_docs: bool = True + proto_file_name: str = None + + @property + def src_dir(self): + return SRC_DIR / self.name + + @property + def metadata_dir(self): + return self.src_dir / 'metadata' + + @property + def output_dir(self): + return GENERATED_DIR / self.name + + @property + def module_dir(self): + return self.output_dir / self.name + + @property + def docs_dir(self): + return DOCS_DIR / self.name + + @property + def proto_file(self): + return self.metadata_dir / (self.proto_file_name or f'{self.name}.proto') + + @property + def grpc_supported(self): + return self.proto_file.exists() + + +# In alphabetical order except put nifake first and nimodinst/nitclk last +# - nifake first to get the most code generator coverage +# - nimodinst last so that the version from nimodinst is used for any global versions (docs/conf.py) +DRIVERS = {d.name: d for d in ( + Driver( + 'nifake', + extra_py_files=('_complextype.py',), + custom_types=('custom_struct.py', 'custom_struct_typedef.py', 'custom_struct_nested_typedef.py'), + has_docs=False), + Driver( + 'nidcpower', + custom_types=('lcr_load_compensation_spot.py', 'lcr_measurement.py')), + Driver( + 'nidigital', + custom_types=('history_ram_cycle_information.py',), + proto_file_name='nidigitalpattern.proto'), + Driver( + 'nidmm', + omit_doc_files=('rep_caps.rst',)), + Driver('nifgen'), + Driver( + 'nirfsa', + extra_py_files=('_complextype.py',), + custom_types=('coefficient_info_type.py', 'waveform_info.py', 'spectrum_info_type.py')), + Driver( + 'nirfsg', + extra_py_files=('_complextype.py',)), + Driver( + 'niscope', + custom_types=('waveform_info.py', 'measurement_stats.py')), + Driver('niswitch'), + Driver( + 'nise', + omit_py_files=('_attributes.py',), + omit_doc_files=('rep_caps.rst',)), + Driver( + 'nimodinst', + omit_py_files=('enums.py', '_attributes.py'), + omit_doc_files=('rep_caps.rst', 'enums.rst')), + Driver( + 'nitclk', + omit_py_files=('enums.py',), + omit_doc_files=('rep_caps.rst', 'enums.rst')), +)} + + +def _trace(action, path): + path = Path(path) + try: + path = path.relative_to(ROOT_DIR) + except ValueError: + pass + print(f'{action:>15}: {path}') + + +def _find_template(driver, template_name): + """Use the driver's own template if it has one, otherwise the shared one.""" + driver_template = driver.src_dir / 'templates' / template_name + if driver_template.exists(): + return driver_template + return TEMPLATE_DIR / template_name + + +def py_files_to_generate(driver): + files = DEFAULT_PY_FILES_TO_GENERATE + driver.extra_py_files + if driver.grpc_supported: + stem = driver.proto_file.stem + files += GRPC_PY_FILES_TO_GENERATE + (f'{stem}_pb2.py', f'{stem}_pb2_grpc.py', 'nidevice_pb2.py', 'nidevice_pb2_grpc.py') + return tuple(f for f in files if f not in driver.omit_py_files) + + +def doc_files_to_generate(driver): + """Returns (file name, template name) pairs.""" + files = DEFAULT_DOC_FILES_TO_GENERATE + if driver.grpc_supported: + files += GRPC_DOC_FILES_TO_GENERATE + return tuple( + (name.format(driver=driver.name), template) + for name, template in files + if name not in driver.omit_doc_files) + + +def unit_test_files_to_copy(driver): + return tuple(sorted((driver.src_dir / 'unit_tests').glob('*.py'))) + + +def example_files(driver): + examples_dir = driver.src_dir / 'examples' + if not examples_dir.is_dir(): + return () + return tuple( + f for f in sorted(examples_dir.rglob('*')) + if f.is_file() and '__pycache__' not in f.parts and f.suffix != '.pyc') + + +def readme_parts(driver): + """Files that are concatenated to create the driver's README.rst.""" + return ( + STATIC_DOCS_DIR / 'status_project.inc', + driver.docs_dir / f'about_{driver.name}.inc', + driver.docs_dir / 'status.inc', + driver.docs_dir / 'installation.inc', + STATIC_DOCS_DIR / 'contributing.inc', + STATIC_DOCS_DIR / f'{driver.name}_usage.inc', + STATIC_DOCS_DIR / 'support.inc', + STATIC_DOCS_DIR / f'{driver.name}_documentation.inc', + STATIC_DOCS_DIR / 'license.inc', + ) + + +def root_readme_parts(): + driver_status_files = tuple(sorted(DOCS_DIR.glob('*/status.inc'))) + return ( + STATIC_DOCS_DIR / 'status_project.inc', + STATIC_DOCS_DIR / 'about.inc', + *driver_status_files, + STATIC_DOCS_DIR / 'installation.inc', + STATIC_DOCS_DIR / 'contributing.inc', + STATIC_DOCS_DIR / 'nidmm_usage.inc', + STATIC_DOCS_DIR / 'support.inc', + STATIC_DOCS_DIR / 'documentation.inc', + STATIC_DOCS_DIR / 'license.inc', + ) + + +def rewrite_pb2_imports(contents, proto_stem): + """Make the generated modules import each other as part of the driver package.""" + for stem in ('nidevice', proto_stem): + contents = re.sub(rb'^import ' + stem.encode() + rb'_pb2', b'from . import ' + stem.encode() + rb'_pb2', contents, flags=re.MULTILINE) + return contents + + +def _load_metadata(driver): + # Every template must start from freshly loaded metadata because loading it modifies it in place. + for name in [n for n in sys.modules if n == 'metadata' or n.startswith('metadata.')]: + del sys.modules[name] + return load_build(str(driver.metadata_dir)) + + +def render_template(driver, template, dest_file): + _trace('Generating', dest_file) + Path(dest_file).parent.mkdir(parents=True, exist_ok=True) + template_params = { + 'metadata': _load_metadata(driver), + 'include_grpc_support': driver.grpc_supported, + } + generate_template(str(template), template_params, str(dest_file)) + + +def copy_file(source, dest_file): + _trace('Copying', dest_file) + Path(dest_file).parent.mkdir(parents=True, exist_ok=True) + shutil.copyfile(source, dest_file) + + +def concatenate_files(sources, dest_file): + _trace('Creating', dest_file) + Path(dest_file).parent.mkdir(parents=True, exist_ok=True) + with open(dest_file, 'wb') as dest: + for source in sources: + dest.write(Path(source).read_bytes()) + + +def generate_pb2_files(driver): + stem = driver.proto_file.stem + proto_dirs = (driver.metadata_dir, SHARED_PROTOS_DIR, NI_APIS_PROTOS_DIR) + for proto_stem in (stem, 'nidevice'): + _trace('Generating', driver.module_dir / f'{proto_stem}_pb2.py and {proto_stem}_pb2_grpc.py') + command = [sys.executable, '-m', 'grpc_tools.protoc'] + command += [f'-I={d}' for d in proto_dirs] + command += [f'--python_out={driver.module_dir}', f'--grpc_python_out={driver.module_dir}', f'{proto_stem}.proto'] + subprocess.run(command, cwd=ROOT_DIR, check=True) + for suffix in ('_pb2.py', '_pb2_grpc.py'): + for proto_stem in (stem, 'nidevice'): + pb2_file = driver.module_dir / f'{proto_stem}{suffix}' + pb2_file.write_bytes(rewrite_pb2_imports(pb2_file.read_bytes(), stem)) + + +def zip_examples(driver): + files = example_files(driver) + if not files: + return + examples_dir = driver.src_dir / 'examples' + zip_path = GENERATED_DIR / 'examples' / f'{driver.name}_examples.zip' + _trace('Zipping', zip_path) + zip_path.parent.mkdir(parents=True, exist_ok=True) + with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED, compresslevel=9) as zip_file: + for f in files: + zip_file.write(f, f.relative_to(examples_dir).as_posix()) + + +def generate_driver(driver): + print() + print(f'Making {driver.name}') + + for file_name in py_files_to_generate(driver): + if file_name.endswith(('_pb2.py', '_pb2_grpc.py')): + continue + dest = driver.module_dir / file_name + render_template(driver, _find_template(driver, Path(file_name).name + '.mako'), dest) + if driver.grpc_supported: + generate_pb2_files(driver) + + for file_name in DEFAULT_PY_FILES_TO_COPY: + copy_file(TEMPLATE_DIR / file_name, driver.module_dir / file_name) + for file_name in driver.custom_types: + copy_file(driver.src_dir / 'custom_types' / file_name, driver.module_dir / file_name) + for source in unit_test_files_to_copy(driver): + copy_file(source, driver.module_dir / 'unit_tests' / source.name) + + render_template(driver, TEMPLATE_DIR / 'setup.py.mako', driver.output_dir / 'setup.py') + render_template(driver, TEMPLATE_DIR / 'tox-system_tests.ini.mako', driver.output_dir / 'tox-system_tests.ini') + + if driver.has_docs: + for file_name, template_name in doc_files_to_generate(driver): + render_template(driver, _find_template(driver, template_name), driver.docs_dir / file_name) + for file_name, template_name in SPHINX_FILES_TO_GENERATE: + render_template(driver, _find_template(driver, template_name), driver.docs_dir / file_name) + + # We can't build the actual readme for nifake since not all the files are created. + # A placeholder is needed for inclusion into the wheel that will never be used. + if driver.has_docs: + concatenate_files(readme_parts(driver), driver.output_dir / 'README.rst') + else: + copy_file(ROOT_DIR / 'LICENSE', driver.output_dir / 'README.rst') + + zip_examples(driver) + + +def generate(drivers, include_root_readme): + # The templates and metadata processing use paths relative to the root of the repository. + os.chdir(ROOT_DIR) + GENERATED_DIR.mkdir(exist_ok=True) + for driver in drivers: + generate_driver(driver) + if include_root_readme: + print() + print('Making Global Files') + concatenate_files(root_readme_parts(), ROOT_DIR / 'README.rst') + + +def clean(drivers): + print('Cleaning...') + shutil.rmtree(GENERATED_DIR, ignore_errors=True) + for pycache_dir in BUILD_DIR.rglob('__pycache__'): + shutil.rmtree(pycache_dir, ignore_errors=True) + for pyc_file in BUILD_DIR.rglob('*.pyc'): + pyc_file.unlink() + for driver in drivers: + if driver.docs_dir.is_dir(): + for doc_file in driver.docs_dir.iterdir(): + if doc_file.is_file(): + doc_file.unlink() + (ROOT_DIR / '.coverage').unlink(missing_ok=True) + (ROOT_DIR / 'README.rst').unlink(missing_ok=True) + # Keep the generated directory available for CI builds + GENERATED_DIR.mkdir(exist_ok=True) + + +def installers(drivers, verbose): + for driver in drivers: + print() + print(f'Making {driver.name} installers') + log_file = driver.output_dir / 'log' / 'sdist_wheel.log' + log_file.parent.mkdir(parents=True, exist_ok=True) + _trace('Creating sdist and wheel', driver.output_dir / 'dist') + result = subprocess.run( + [sys.executable, '-m', 'build'], + cwd=driver.output_dir, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + log_file.write_bytes(result.stdout) + if verbose or result.returncode != 0: + sys.stdout.write(result.stdout.decode(errors='replace')) + if result.returncode != 0: + sys.exit(f'Building the installers for {driver.name} failed. See {log_file}') + + +def parse_args(argv=None): + common = argparse.ArgumentParser(add_help=False) + common.add_argument( + '-v', '--verbose', action='count', default=0, + help='Verbose output') + common.add_argument( + '--log-file', default=None, + help='Send logging to listed file instead of stdout') + + drivers_parent = argparse.ArgumentParser(add_help=False) + drivers_parent.add_argument( + '--driver', action='append', choices=list(DRIVERS), dest='drivers', metavar='DRIVER', + help=f'Driver to process. Can be repeated. Default is all drivers: {", ".join(DRIVERS)}') + + parser = argparse.ArgumentParser(prog='build.codegen', description=__doc__.splitlines()[0]) + subparsers = parser.add_subparsers(dest='command', required=True) + subparsers.add_parser( + 'generate', parents=[common, drivers_parent], + help='Generate bindings, documentation and READMEs. The root README.rst is only generated for all drivers.') + subparsers.add_parser( + 'clean', parents=[common, drivers_parent], + help='Delete generated files') + subparsers.add_parser( + 'installers', parents=[common, drivers_parent], + help='Build the sdist and wheel for each driver from generated/. Run generate first.') + return parser.parse_args(argv) + + +def main(argv=None): + args = parse_args(argv) + + if args.verbose > 1: + configure_logging(logging.DEBUG, args.log_file) + elif args.verbose == 1: + configure_logging(logging.INFO, args.log_file) + else: + configure_logging(logging.WARNING, args.log_file) + + drivers = [DRIVERS[name] for name in args.drivers] if args.drivers else list(DRIVERS.values()) + if args.command == 'generate': + generate(drivers, include_root_readme=not args.drivers) + elif args.command == 'clean': + clean(drivers) + elif args.command == 'installers': + installers(drivers, args.verbose > 0) + + +if __name__ == '__main__': + main() diff --git a/build/unit_tests/test_codegen.py b/build/unit_tests/test_codegen.py new file mode 100644 index 0000000000..d1d64ad689 --- /dev/null +++ b/build/unit_tests/test_codegen.py @@ -0,0 +1,198 @@ +from build import codegen + + +def test_drivers_match_source_folders(): + src_drivers = {d.name for d in codegen.SRC_DIR.iterdir() if (d / 'metadata').is_dir()} + + assert set(codegen.DRIVERS) == src_drivers + + +def test_nifake_is_first_and_nimodinst_nitclk_are_last(): + names = list(codegen.DRIVERS) + + assert names[0] == 'nifake' + assert names[-2:] == ['nimodinst', 'nitclk'] + + +def test_grpc_supported_follows_proto_file(): + assert codegen.DRIVERS['nidmm'].grpc_supported + assert codegen.DRIVERS['nidigital'].proto_file.name == 'nidigitalpattern.proto' + assert not codegen.DRIVERS['nitclk'].grpc_supported + + +def test_py_files_to_generate_default_driver_with_grpc(): + files = codegen.py_files_to_generate(codegen.DRIVERS['nidmm']) + + assert 'session.py' in files + assert 'unit_tests/_mock_helper.py' in files + assert 'nidmm_pb2.py' in files + assert 'nidevice_pb2_grpc.py' in files + assert '_grpc_stub_interpreter.py' in files + assert '_complextype.py' not in files + + +def test_py_files_to_generate_uses_proto_file_name(): + files = codegen.py_files_to_generate(codegen.DRIVERS['nidigital']) + + assert 'nidigitalpattern_pb2.py' in files + assert 'nidigitalpattern_pb2_grpc.py' in files + assert 'nidigital_pb2.py' not in files + + +def test_py_files_to_generate_extra_and_omitted_files(): + assert '_complextype.py' in codegen.py_files_to_generate(codegen.DRIVERS['nifake']) + assert '_complextype.py' in codegen.py_files_to_generate(codegen.DRIVERS['nirfsa']) + assert '_attributes.py' not in codegen.py_files_to_generate(codegen.DRIVERS['nise']) + assert 'enums.py' in codegen.py_files_to_generate(codegen.DRIVERS['nise']) + assert 'enums.py' not in codegen.py_files_to_generate(codegen.DRIVERS['nitclk']) + assert '_attributes.py' in codegen.py_files_to_generate(codegen.DRIVERS['nitclk']) + nimodinst_files = codegen.py_files_to_generate(codegen.DRIVERS['nimodinst']) + assert 'enums.py' not in nimodinst_files + assert '_attributes.py' not in nimodinst_files + + +def test_py_files_to_generate_without_grpc_has_no_pb2_files(): + files = codegen.py_files_to_generate(codegen.DRIVERS['nitclk']) + + assert not [f for f in files if 'pb2' in f or 'grpc' in f] + + +def test_doc_files_to_generate_names_are_driver_specific(): + files = dict(codegen.doc_files_to_generate(codegen.DRIVERS['nidmm'])) + + assert files['about_nidmm.inc'] == 'about_driver.inc.mako' + assert files['nidmm.rst'] == 'driver.rst.mako' + assert files['grpc_session_options.rst'] == 'grpc_session_options.rst.mako' + assert 'rep_caps.rst' not in files + + +def test_doc_files_to_generate_omitted_files(): + nitclk_files = dict(codegen.doc_files_to_generate(codegen.DRIVERS['nitclk'])) + + assert 'enums.rst' not in nitclk_files + assert 'rep_caps.rst' not in nitclk_files + assert 'grpc_session_options.rst' not in nitclk_files + assert 'rep_caps.rst' in dict(codegen.doc_files_to_generate(codegen.DRIVERS['niswitch'])) + + +def test_find_template_prefers_driver_template(): + nitclk = codegen.DRIVERS['nitclk'] + + assert codegen._find_template(nitclk, 'session.py.mako') == nitclk.src_dir / 'templates' / 'session.py.mako' + assert codegen._find_template(nitclk, 'errors.py.mako') == codegen.TEMPLATE_DIR / 'errors.py.mako' + assert codegen._find_template(codegen.DRIVERS['nidmm'], 'session.py.mako') == codegen.TEMPLATE_DIR / 'session.py.mako' + + +def test_custom_types_exist(): + for driver in codegen.DRIVERS.values(): + for custom_type in driver.custom_types: + assert (driver.src_dir / 'custom_types' / custom_type).is_file() + + +def test_nifake_has_no_docs_or_examples(): + nifake = codegen.DRIVERS['nifake'] + + assert not nifake.has_docs + assert codegen.example_files(nifake) == () + + +def test_example_files_are_sorted_and_skip_bytecode(): + files = codegen.example_files(codegen.DRIVERS['nidmm']) + + assert files + assert list(files) == sorted(files) + assert all(f.suffix != '.pyc' and '__pycache__' not in f.parts for f in files) + + +def test_readme_parts_for_driver(): + parts = codegen.readme_parts(codegen.DRIVERS['nidmm']) + + assert [p.name for p in parts] == [ + 'status_project.inc', + 'about_nidmm.inc', + 'status.inc', + 'installation.inc', + 'contributing.inc', + 'nidmm_usage.inc', + 'support.inc', + 'nidmm_documentation.inc', + 'license.inc', + ] + + +def test_root_readme_parts_include_each_driver_status_in_order(monkeypatch, tmp_path): + for name in ('nitclk', 'nidmm', 'niscope'): + (tmp_path / name).mkdir() + (tmp_path / name / 'status.inc').write_text('') + (tmp_path / 'nise').mkdir() + monkeypatch.setattr(codegen, 'DOCS_DIR', tmp_path) + + status_files = [p for p in codegen.root_readme_parts() if p.name == 'status.inc'] + + assert [p.parent.name for p in status_files] == ['nidmm', 'niscope', 'nitclk'] + + +def test_rewrite_pb2_imports(): + contents = b'import session_pb2 as s\nimport nidevice_pb2 as n\nimport nifake_pb2 as f\nimport nidevice_pb2_grpc\n# import nifake_pb2\n' + + assert codegen.rewrite_pb2_imports(contents, 'nifake') == ( + b'import session_pb2 as s\n' + b'from . import nidevice_pb2 as n\n' + b'from . import nifake_pb2 as f\n' + b'from . import nidevice_pb2_grpc\n' + b'# import nifake_pb2\n') + + +def test_concatenate_files_is_binary_exact(tmp_path): + (tmp_path / 'a.inc').write_bytes(b'first\n') + (tmp_path / 'b.inc').write_bytes(b'second\r\nthird') + dest = tmp_path / 'out' / 'README.rst' + + codegen.concatenate_files([tmp_path / 'a.inc', tmp_path / 'b.inc'], dest) + + assert dest.read_bytes() == b'first\nsecond\r\nthird' + + +def test_clean_removes_generated_files_but_not_doc_subfolders(monkeypatch, tmp_path): + root = tmp_path + generated = root / 'generated' + docs = root / 'docs' + build = root / 'build' + (generated / 'nidmm').mkdir(parents=True) + (generated / 'nidmm' / 'setup.py').write_text('') + (docs / 'nidmm' / 'subfolder').mkdir(parents=True) + (docs / 'nidmm' / 'index.rst').write_text('') + (docs / 'nidmm' / '.readthedocs.yaml').write_text('') + (docs / 'nidcpower').mkdir() + (docs / 'nidcpower' / 'index.rst').write_text('') + (build / '__pycache__').mkdir(parents=True) + (build / '__pycache__' / 'x.pyc').write_text('') + (root / 'README.rst').write_text('') + (root / '.coverage').write_text('') + for name, value in (('ROOT_DIR', root), ('GENERATED_DIR', generated), ('DOCS_DIR', docs), ('BUILD_DIR', build)): + monkeypatch.setattr(codegen, name, value) + + codegen.clean([codegen.DRIVERS['nidmm']]) + + assert generated.is_dir() + assert not list(generated.iterdir()) + assert [p.name for p in (docs / 'nidmm').iterdir()] == ['subfolder'] + assert (docs / 'nidcpower' / 'index.rst').exists() + assert not (build / '__pycache__').exists() + assert not (root / 'README.rst').exists() + assert not (root / '.coverage').exists() + + +def test_parse_args_defaults_to_all_drivers(): + args = codegen.parse_args(['generate']) + + assert args.command == 'generate' + assert args.drivers is None + + +def test_parse_args_repeated_driver(): + args = codegen.parse_args(['installers', '--driver', 'nidmm', '--driver', 'nitclk', '-v']) + + assert args.command == 'installers' + assert args.drivers == ['nidmm', 'nitclk'] + assert args.verbose == 1 diff --git a/tox.ini b/tox.ini index b42df19fdd..da75856019 100644 --- a/tox.ini +++ b/tox.ini @@ -87,15 +87,15 @@ commands = test: coverage html --directory=generated/htmlcov/unit_tests/nitclk clean: python --version clean: python -c "import platform; print(platform.architecture())" - clean: make clean {posargs} + clean: python -m build.codegen clean {posargs} codegen: python --version codegen: python -c "import platform; print(platform.architecture())" codegen: python -m pip install --disable-pip-version-check --upgrade pip - codegen: make {posargs} + codegen: python -m build.codegen generate {posargs} installers: python --version installers: python -c "import platform; print(platform.architecture())" installers: python -m pip install --disable-pip-version-check --upgrade pip - installers: make installers {posargs} + installers: python -m build.codegen installers {posargs} flake8: python --version flake8: python -c "import platform; print(platform.architecture())" flake8: python -m pip install --disable-pip-version-check --upgrade pip @@ -156,11 +156,13 @@ deps = build_test: mako build_test: hacking build_test: pep8-naming + clean: mako codegen: mako codegen: packaging codegen: setuptools < 82 # grpcio-tools 1.59.0 needs pkg_resources which was removed in setuptools v82 codegen: grpcio-tools == 1.59.0 # First version to support Python 3.12 installers: build + installers: mako flake8: hacking flake8: pep8-naming docs: sphinx @@ -181,9 +183,6 @@ depends = allowlist_externals = build_test: mv test: mv - codegen: make - installers: make - clean: make [flake8] show_source = true From 27bcf6ff0155eefd8db14867fd902442ff4fb893 Mon Sep 17 00:00:00 2001 From: Jay Fitzgerald <34140133+ni-jfitzger@users.noreply.github.com> Date: Wed, 14 Oct 2026 10:38:10 -0500 Subject: [PATCH 03/10] Speed up codegen by skipping writing processed_metadata on every template. --- build/codegen.py | 6 ++++++ build/helper/metadata_add_all.py | 9 +++++++++ build/unit_tests/test_metadata_add_all.py | 18 ++++++++++++++++++ tox.ini | 2 ++ 4 files changed, 35 insertions(+) diff --git a/build/codegen.py b/build/codegen.py index 8cb2a02735..b49284f9b2 100644 --- a/build/codegen.py +++ b/build/codegen.py @@ -17,6 +17,7 @@ from build import configure_logging from build import generate_template +from build.helper import metadata_add_all from build.utilities import load_build ROOT_DIR = Path(__file__).resolve().parent.parent @@ -29,6 +30,8 @@ SHARED_PROTOS_DIR = SRC_DIR / 'shared_protos' NI_APIS_PROTOS_DIR = ROOT_DIR / 'third_party' / 'ni-apis' / 'ni' / 'grpcdevice' / 'v1' +_drivers_with_processed_metadata = set() + DEFAULT_PY_FILES_TO_GENERATE = ( '_attributes.py', 'enums.py', @@ -255,6 +258,9 @@ def _load_metadata(driver): # Every template must start from freshly loaded metadata because loading it modifies it in place. for name in [n for n in sys.modules if n == 'metadata' or n.startswith('metadata.')]: del sys.modules[name] + # The processed metadata files are the same every time, so only write them once per driver. + metadata_add_all.write_processed_metadata = driver.name not in _drivers_with_processed_metadata + _drivers_with_processed_metadata.add(driver.name) return load_build(str(driver.metadata_dir)) diff --git a/build/helper/metadata_add_all.py b/build/helper/metadata_add_all.py index f28de7d9d8..cc8ae91c6c 100644 --- a/build/helper/metadata_add_all.py +++ b/build/helper/metadata_add_all.py @@ -23,6 +23,10 @@ pp = pprint.PrettyPrinter(indent=4, width=80) +# Writing generated/processed_metadata is a debugging aid and is slow. Code generation turns this off +# when it loads the same metadata again. +write_processed_metadata = True + # Functions to add information to metadata structures that are specific to our codegen needs. @@ -803,6 +807,11 @@ def add_all_metadata(functions, attributes, enums, config, persist_output=True): if not persist_output: return config + if not write_processed_metadata: + # The files are only a debugging aid, but keep the metadata the same as when they are written + del config['modules'] + return config + if not os.path.exists(metadata_dir): os.makedirs(metadata_dir) diff --git a/build/unit_tests/test_metadata_add_all.py b/build/unit_tests/test_metadata_add_all.py index 252155c66f..44374a0284 100644 --- a/build/unit_tests/test_metadata_add_all.py +++ b/build/unit_tests/test_metadata_add_all.py @@ -1,3 +1,4 @@ +from build.helper import metadata_add_all from build.helper.metadata_add_all import * from build.helper.metadata_add_all import _add_enum_codegen_method from build.helper.metadata_add_all import _get_attributes_that_use_enums @@ -1106,6 +1107,23 @@ def test_add_all_metadata(): expected=expected) +def test_add_all_metadata_without_writing_processed_metadata_matches_persisted_config(monkeypatch, tmp_path): + monkeypatch.chdir(tmp_path) + + def _processed_config(write): + monkeypatch.setattr(metadata_add_all, 'write_processed_metadata', write) + config = copy.deepcopy(config_input) + return add_all_metadata(copy.deepcopy(functions_input), copy.deepcopy(attributes_input), copy.deepcopy(enums_input), config) + + skipped = _processed_config(write=False) + assert not (tmp_path / 'generated').exists() + written = _processed_config(write=True) + assert (tmp_path / 'generated' / 'processed_metadata').is_dir() + + assert 'modules' not in skipped + _compare_dicts(skipped, written) + + def _setup_inputs_for_enum_codegen_method_tests(): actual_enums = copy.deepcopy(enums_input) actual_config = copy.deepcopy(config_input) diff --git a/tox.ini b/tox.ini index da75856019..1f1a5eadd4 100644 --- a/tox.ini +++ b/tox.ini @@ -157,12 +157,14 @@ deps = build_test: hacking build_test: pep8-naming clean: mako + clean: packaging codegen: mako codegen: packaging codegen: setuptools < 82 # grpcio-tools 1.59.0 needs pkg_resources which was removed in setuptools v82 codegen: grpcio-tools == 1.59.0 # First version to support Python 3.12 installers: build installers: mako + installers: packaging flake8: hacking flake8: pep8-naming docs: sphinx From b39dbe487c8c67d40460187fa875c01e8e969c7d Mon Sep 17 00:00:00 2001 From: Jay Fitzgerald <34140133+ni-jfitzger@users.noreply.github.com> Date: Fri, 23 Oct 2026 10:30:04 -0500 Subject: [PATCH 04/10] Rename SPHINX_FILES_TO_GENERATE to reflect inclusion of a non-SPHINX file --- build/codegen.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/codegen.py b/build/codegen.py index b49284f9b2..8af312b76c 100644 --- a/build/codegen.py +++ b/build/codegen.py @@ -76,7 +76,7 @@ ('grpc_session_options.rst', 'grpc_session_options.rst.mako'), ) -SPHINX_FILES_TO_GENERATE = ( +DOC_CONFIG_FILES_TO_GENERATE = ( ('conf.py', 'conf.py.mako'), ('.readthedocs.yaml', '.readthedocs.yaml.mako'), ) @@ -341,7 +341,7 @@ def generate_driver(driver): if driver.has_docs: for file_name, template_name in doc_files_to_generate(driver): render_template(driver, _find_template(driver, template_name), driver.docs_dir / file_name) - for file_name, template_name in SPHINX_FILES_TO_GENERATE: + for file_name, template_name in DOC_CONFIG_FILES_TO_GENERATE: render_template(driver, _find_template(driver, template_name), driver.docs_dir / file_name) # We can't build the actual readme for nifake since not all the files are created. From d07b4c334c5b69bb1e6007a55033d2b46be6dee6 Mon Sep 17 00:00:00 2001 From: Jay Fitzgerald <34140133+ni-jfitzger@users.noreply.github.com> Date: Fri, 23 Oct 2026 10:35:16 -0500 Subject: [PATCH 05/10] Change name of BUILD_DIR variable back to the original BUILD_HELPER_DIR --- build/codegen.py | 8 ++++---- build/unit_tests/test_codegen.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/codegen.py b/build/codegen.py index 8af312b76c..a5685251bf 100644 --- a/build/codegen.py +++ b/build/codegen.py @@ -21,8 +21,8 @@ from build.utilities import load_build ROOT_DIR = Path(__file__).resolve().parent.parent -BUILD_DIR = ROOT_DIR / 'build' -TEMPLATE_DIR = BUILD_DIR / 'templates' +BUILD_HELPER_DIR = ROOT_DIR / 'build' +TEMPLATE_DIR = BUILD_HELPER_DIR / 'templates' GENERATED_DIR = ROOT_DIR / 'generated' DOCS_DIR = ROOT_DIR / 'docs' STATIC_DOCS_DIR = DOCS_DIR / '_static' @@ -369,9 +369,9 @@ def generate(drivers, include_root_readme): def clean(drivers): print('Cleaning...') shutil.rmtree(GENERATED_DIR, ignore_errors=True) - for pycache_dir in BUILD_DIR.rglob('__pycache__'): + for pycache_dir in BUILD_HELPER_DIR.rglob('__pycache__'): shutil.rmtree(pycache_dir, ignore_errors=True) - for pyc_file in BUILD_DIR.rglob('*.pyc'): + for pyc_file in BUILD_HELPER_DIR.rglob('*.pyc'): pyc_file.unlink() for driver in drivers: if driver.docs_dir.is_dir(): diff --git a/build/unit_tests/test_codegen.py b/build/unit_tests/test_codegen.py index d1d64ad689..a810b9cbc0 100644 --- a/build/unit_tests/test_codegen.py +++ b/build/unit_tests/test_codegen.py @@ -169,7 +169,7 @@ def test_clean_removes_generated_files_but_not_doc_subfolders(monkeypatch, tmp_p (build / '__pycache__' / 'x.pyc').write_text('') (root / 'README.rst').write_text('') (root / '.coverage').write_text('') - for name, value in (('ROOT_DIR', root), ('GENERATED_DIR', generated), ('DOCS_DIR', docs), ('BUILD_DIR', build)): + for name, value in (('ROOT_DIR', root), ('GENERATED_DIR', generated), ('DOCS_DIR', docs), ('BUILD_HELPER_DIR', build)): monkeypatch.setattr(codegen, name, value) codegen.clean([codegen.DRIVERS['nidmm']]) From 8fad16149334d4b50cb19e8f1f8e808b6bce8860 Mon Sep 17 00:00:00 2001 From: Jay Fitzgerald <34140133+ni-jfitzger@users.noreply.github.com> Date: Fri, 23 Oct 2026 12:18:22 -0500 Subject: [PATCH 06/10] Only unload and load metadata once for each driver api, when doing codegen --- build/codegen.py | 25 ++++++++++------------- build/helper/metadata_add_all.py | 9 -------- build/unit_tests/test_metadata_add_all.py | 18 ---------------- 3 files changed, 11 insertions(+), 41 deletions(-) diff --git a/build/codegen.py b/build/codegen.py index a5685251bf..2506588c83 100644 --- a/build/codegen.py +++ b/build/codegen.py @@ -17,7 +17,6 @@ from build import configure_logging from build import generate_template -from build.helper import metadata_add_all from build.utilities import load_build ROOT_DIR = Path(__file__).resolve().parent.parent @@ -30,8 +29,6 @@ SHARED_PROTOS_DIR = SRC_DIR / 'shared_protos' NI_APIS_PROTOS_DIR = ROOT_DIR / 'third_party' / 'ni-apis' / 'ni' / 'grpcdevice' / 'v1' -_drivers_with_processed_metadata = set() - DEFAULT_PY_FILES_TO_GENERATE = ( '_attributes.py', 'enums.py', @@ -255,20 +252,17 @@ def rewrite_pb2_imports(contents, proto_stem): def _load_metadata(driver): - # Every template must start from freshly loaded metadata because loading it modifies it in place. + # Every driver's metadata package is named "metadata", so the previous driver's must be unloaded. for name in [n for n in sys.modules if n == 'metadata' or n.startswith('metadata.')]: del sys.modules[name] - # The processed metadata files are the same every time, so only write them once per driver. - metadata_add_all.write_processed_metadata = driver.name not in _drivers_with_processed_metadata - _drivers_with_processed_metadata.add(driver.name) return load_build(str(driver.metadata_dir)) -def render_template(driver, template, dest_file): +def render_template(driver, metadata, template, dest_file): _trace('Generating', dest_file) Path(dest_file).parent.mkdir(parents=True, exist_ok=True) template_params = { - 'metadata': _load_metadata(driver), + 'metadata': metadata, 'include_grpc_support': driver.grpc_supported, } generate_template(str(template), template_params, str(dest_file)) @@ -319,12 +313,15 @@ def zip_examples(driver): def generate_driver(driver): print() print(f'Making {driver.name}') + # We only unload and load metadata once for each driver api. + # This is only safe if templates don't modify the metadata. + metadata = _load_metadata(driver) for file_name in py_files_to_generate(driver): if file_name.endswith(('_pb2.py', '_pb2_grpc.py')): continue dest = driver.module_dir / file_name - render_template(driver, _find_template(driver, Path(file_name).name + '.mako'), dest) + render_template(driver, metadata, _find_template(driver, Path(file_name).name + '.mako'), dest) if driver.grpc_supported: generate_pb2_files(driver) @@ -335,14 +332,14 @@ def generate_driver(driver): for source in unit_test_files_to_copy(driver): copy_file(source, driver.module_dir / 'unit_tests' / source.name) - render_template(driver, TEMPLATE_DIR / 'setup.py.mako', driver.output_dir / 'setup.py') - render_template(driver, TEMPLATE_DIR / 'tox-system_tests.ini.mako', driver.output_dir / 'tox-system_tests.ini') + render_template(driver, metadata, TEMPLATE_DIR / 'setup.py.mako', driver.output_dir / 'setup.py') + render_template(driver, metadata, TEMPLATE_DIR / 'tox-system_tests.ini.mako', driver.output_dir / 'tox-system_tests.ini') if driver.has_docs: for file_name, template_name in doc_files_to_generate(driver): - render_template(driver, _find_template(driver, template_name), driver.docs_dir / file_name) + render_template(driver, metadata, _find_template(driver, template_name), driver.docs_dir / file_name) for file_name, template_name in DOC_CONFIG_FILES_TO_GENERATE: - render_template(driver, _find_template(driver, template_name), driver.docs_dir / file_name) + render_template(driver, metadata, _find_template(driver, template_name), driver.docs_dir / file_name) # We can't build the actual readme for nifake since not all the files are created. # A placeholder is needed for inclusion into the wheel that will never be used. diff --git a/build/helper/metadata_add_all.py b/build/helper/metadata_add_all.py index 432ffe543e..3d85e7f97b 100644 --- a/build/helper/metadata_add_all.py +++ b/build/helper/metadata_add_all.py @@ -23,10 +23,6 @@ pp = pprint.PrettyPrinter(indent=4, width=80) -# Writing generated/processed_metadata is a debugging aid and is slow. Code generation turns this off -# when it loads the same metadata again. -write_processed_metadata = True - # Functions to add information to metadata structures that are specific to our codegen needs. @@ -808,11 +804,6 @@ def add_all_metadata(functions, attributes, enums, config, persist_output=True): if not persist_output: return config - if not write_processed_metadata: - # The files are only a debugging aid, but keep the metadata the same as when they are written - del config['modules'] - return config - if not os.path.exists(metadata_dir): os.makedirs(metadata_dir) diff --git a/build/unit_tests/test_metadata_add_all.py b/build/unit_tests/test_metadata_add_all.py index 44374a0284..252155c66f 100644 --- a/build/unit_tests/test_metadata_add_all.py +++ b/build/unit_tests/test_metadata_add_all.py @@ -1,4 +1,3 @@ -from build.helper import metadata_add_all from build.helper.metadata_add_all import * from build.helper.metadata_add_all import _add_enum_codegen_method from build.helper.metadata_add_all import _get_attributes_that_use_enums @@ -1107,23 +1106,6 @@ def test_add_all_metadata(): expected=expected) -def test_add_all_metadata_without_writing_processed_metadata_matches_persisted_config(monkeypatch, tmp_path): - monkeypatch.chdir(tmp_path) - - def _processed_config(write): - monkeypatch.setattr(metadata_add_all, 'write_processed_metadata', write) - config = copy.deepcopy(config_input) - return add_all_metadata(copy.deepcopy(functions_input), copy.deepcopy(attributes_input), copy.deepcopy(enums_input), config) - - skipped = _processed_config(write=False) - assert not (tmp_path / 'generated').exists() - written = _processed_config(write=True) - assert (tmp_path / 'generated' / 'processed_metadata').is_dir() - - assert 'modules' not in skipped - _compare_dicts(skipped, written) - - def _setup_inputs_for_enum_codegen_method_tests(): actual_enums = copy.deepcopy(enums_input) actual_config = copy.deepcopy(config_input) From a903de163937a445635ef18fd938e0ea47910f04 Mon Sep 17 00:00:00 2001 From: Jay Fitzgerald <34140133+ni-jfitzger@users.noreply.github.com> Date: Fri, 23 Oct 2026 12:35:12 -0500 Subject: [PATCH 07/10] Revert "Only unload and load metadata once for each driver api, when doing codegen" Turns out the templates do tweak some metadata, even though we can't see a difference in the output. This reverts commit 8fad16149334d4b50cb19e8f1f8e808b6bce8860. --- build/codegen.py | 25 +++++++++++++---------- build/helper/metadata_add_all.py | 9 ++++++++ build/unit_tests/test_metadata_add_all.py | 18 ++++++++++++++++ 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/build/codegen.py b/build/codegen.py index 2506588c83..a5685251bf 100644 --- a/build/codegen.py +++ b/build/codegen.py @@ -17,6 +17,7 @@ from build import configure_logging from build import generate_template +from build.helper import metadata_add_all from build.utilities import load_build ROOT_DIR = Path(__file__).resolve().parent.parent @@ -29,6 +30,8 @@ SHARED_PROTOS_DIR = SRC_DIR / 'shared_protos' NI_APIS_PROTOS_DIR = ROOT_DIR / 'third_party' / 'ni-apis' / 'ni' / 'grpcdevice' / 'v1' +_drivers_with_processed_metadata = set() + DEFAULT_PY_FILES_TO_GENERATE = ( '_attributes.py', 'enums.py', @@ -252,17 +255,20 @@ def rewrite_pb2_imports(contents, proto_stem): def _load_metadata(driver): - # Every driver's metadata package is named "metadata", so the previous driver's must be unloaded. + # Every template must start from freshly loaded metadata because loading it modifies it in place. for name in [n for n in sys.modules if n == 'metadata' or n.startswith('metadata.')]: del sys.modules[name] + # The processed metadata files are the same every time, so only write them once per driver. + metadata_add_all.write_processed_metadata = driver.name not in _drivers_with_processed_metadata + _drivers_with_processed_metadata.add(driver.name) return load_build(str(driver.metadata_dir)) -def render_template(driver, metadata, template, dest_file): +def render_template(driver, template, dest_file): _trace('Generating', dest_file) Path(dest_file).parent.mkdir(parents=True, exist_ok=True) template_params = { - 'metadata': metadata, + 'metadata': _load_metadata(driver), 'include_grpc_support': driver.grpc_supported, } generate_template(str(template), template_params, str(dest_file)) @@ -313,15 +319,12 @@ def zip_examples(driver): def generate_driver(driver): print() print(f'Making {driver.name}') - # We only unload and load metadata once for each driver api. - # This is only safe if templates don't modify the metadata. - metadata = _load_metadata(driver) for file_name in py_files_to_generate(driver): if file_name.endswith(('_pb2.py', '_pb2_grpc.py')): continue dest = driver.module_dir / file_name - render_template(driver, metadata, _find_template(driver, Path(file_name).name + '.mako'), dest) + render_template(driver, _find_template(driver, Path(file_name).name + '.mako'), dest) if driver.grpc_supported: generate_pb2_files(driver) @@ -332,14 +335,14 @@ def generate_driver(driver): for source in unit_test_files_to_copy(driver): copy_file(source, driver.module_dir / 'unit_tests' / source.name) - render_template(driver, metadata, TEMPLATE_DIR / 'setup.py.mako', driver.output_dir / 'setup.py') - render_template(driver, metadata, TEMPLATE_DIR / 'tox-system_tests.ini.mako', driver.output_dir / 'tox-system_tests.ini') + render_template(driver, TEMPLATE_DIR / 'setup.py.mako', driver.output_dir / 'setup.py') + render_template(driver, TEMPLATE_DIR / 'tox-system_tests.ini.mako', driver.output_dir / 'tox-system_tests.ini') if driver.has_docs: for file_name, template_name in doc_files_to_generate(driver): - render_template(driver, metadata, _find_template(driver, template_name), driver.docs_dir / file_name) + render_template(driver, _find_template(driver, template_name), driver.docs_dir / file_name) for file_name, template_name in DOC_CONFIG_FILES_TO_GENERATE: - render_template(driver, metadata, _find_template(driver, template_name), driver.docs_dir / file_name) + render_template(driver, _find_template(driver, template_name), driver.docs_dir / file_name) # We can't build the actual readme for nifake since not all the files are created. # A placeholder is needed for inclusion into the wheel that will never be used. diff --git a/build/helper/metadata_add_all.py b/build/helper/metadata_add_all.py index 3d85e7f97b..432ffe543e 100644 --- a/build/helper/metadata_add_all.py +++ b/build/helper/metadata_add_all.py @@ -23,6 +23,10 @@ pp = pprint.PrettyPrinter(indent=4, width=80) +# Writing generated/processed_metadata is a debugging aid and is slow. Code generation turns this off +# when it loads the same metadata again. +write_processed_metadata = True + # Functions to add information to metadata structures that are specific to our codegen needs. @@ -804,6 +808,11 @@ def add_all_metadata(functions, attributes, enums, config, persist_output=True): if not persist_output: return config + if not write_processed_metadata: + # The files are only a debugging aid, but keep the metadata the same as when they are written + del config['modules'] + return config + if not os.path.exists(metadata_dir): os.makedirs(metadata_dir) diff --git a/build/unit_tests/test_metadata_add_all.py b/build/unit_tests/test_metadata_add_all.py index 252155c66f..44374a0284 100644 --- a/build/unit_tests/test_metadata_add_all.py +++ b/build/unit_tests/test_metadata_add_all.py @@ -1,3 +1,4 @@ +from build.helper import metadata_add_all from build.helper.metadata_add_all import * from build.helper.metadata_add_all import _add_enum_codegen_method from build.helper.metadata_add_all import _get_attributes_that_use_enums @@ -1106,6 +1107,23 @@ def test_add_all_metadata(): expected=expected) +def test_add_all_metadata_without_writing_processed_metadata_matches_persisted_config(monkeypatch, tmp_path): + monkeypatch.chdir(tmp_path) + + def _processed_config(write): + monkeypatch.setattr(metadata_add_all, 'write_processed_metadata', write) + config = copy.deepcopy(config_input) + return add_all_metadata(copy.deepcopy(functions_input), copy.deepcopy(attributes_input), copy.deepcopy(enums_input), config) + + skipped = _processed_config(write=False) + assert not (tmp_path / 'generated').exists() + written = _processed_config(write=True) + assert (tmp_path / 'generated' / 'processed_metadata').is_dir() + + assert 'modules' not in skipped + _compare_dicts(skipped, written) + + def _setup_inputs_for_enum_codegen_method_tests(): actual_enums = copy.deepcopy(enums_input) actual_config = copy.deepcopy(config_input) From f7940e2eb0fa43aaf370ffb9dd9a84d300ce2ec6 Mon Sep 17 00:00:00 2001 From: Jay Fitzgerald <34140133+ni-jfitzger@users.noreply.github.com> Date: Fri, 23 Oct 2026 17:32:35 -0500 Subject: [PATCH 08/10] Include codegen.py in coverage analysis. --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 1f1a5eadd4..172ace82ef 100644 --- a/tox.ini +++ b/tox.ini @@ -45,7 +45,7 @@ commands = # doctest validation of code docstrings build_test: coverage run --rcfile=tools/coverage_unit_tests.rc --source build.helper -m pytest --pyargs build.helper # actual unit tests - build_test: coverage run --append --rcfile=tools/coverage_unit_tests.rc --source build.helper -m pytest build/unit_tests {posargs} -s + build_test: coverage run --append --rcfile=tools/coverage_unit_tests.rc --source build.helper --source build.codegen -m pytest build/unit_tests {posargs} -s # Display the report on console build_test: coverage report # Create the report to upload From 507b44c944f06dc34f6e91bbc80f44e985dd49dc Mon Sep 17 00:00:00 2001 From: Jay Fitzgerald <34140133+ni-jfitzger@users.noreply.github.com> Date: Thu, 29 Oct 2026 11:19:45 -0500 Subject: [PATCH 09/10] Fix coverage reporting. --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 172ace82ef..19c71e62c7 100644 --- a/tox.ini +++ b/tox.ini @@ -45,7 +45,7 @@ commands = # doctest validation of code docstrings build_test: coverage run --rcfile=tools/coverage_unit_tests.rc --source build.helper -m pytest --pyargs build.helper # actual unit tests - build_test: coverage run --append --rcfile=tools/coverage_unit_tests.rc --source build.helper --source build.codegen -m pytest build/unit_tests {posargs} -s + build_test: coverage run --append --rcfile=tools/coverage_unit_tests.rc --source build.helper,build.codegen -m pytest build/unit_tests {posargs} -s # Display the report on console build_test: coverage report # Create the report to upload From 3f1100698cb5be6539ace9defdc3f295f1ab7eff Mon Sep 17 00:00:00 2001 From: Jay Fitzgerald <34140133+ni-jfitzger@users.noreply.github.com> Date: Thu, 29 Oct 2026 16:31:58 -0500 Subject: [PATCH 10/10] Cap pytest dep version until they fix the new bug --- build/templates/tox-system_tests.ini.mako | 2 +- generated/nidcpower/tox-system_tests.ini | 2 +- generated/nidigital/tox-system_tests.ini | 2 +- generated/nidmm/tox-system_tests.ini | 2 +- generated/nifake/tox-system_tests.ini | 2 +- generated/nifgen/tox-system_tests.ini | 2 +- generated/nimodinst/tox-system_tests.ini | 2 +- generated/nirfsa/tox-system_tests.ini | 2 +- generated/nirfsg/tox-system_tests.ini | 2 +- generated/niscope/tox-system_tests.ini | 2 +- generated/nise/tox-system_tests.ini | 2 +- generated/niswitch/tox-system_tests.ini | 2 +- generated/nitclk/tox-system_tests.ini | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/build/templates/tox-system_tests.ini.mako b/build/templates/tox-system_tests.ini.mako index c2b3d9131d..dd4bf224c1 100644 --- a/build/templates/tox-system_tests.ini.mako +++ b/build/templates/tox-system_tests.ini.mako @@ -77,7 +77,7 @@ deps = % if module_name == 'nidigital': ${module_name}-system_tests: nisync % endif - ${module_name}-system_tests: pytest + ${module_name}-system_tests: pytest<9.2.0 # Version capped until they fix https://github.com/pytest-dev/pytest/issues/15295 ${module_name}-system_tests: coverage ${module_name}-system_tests: numpy ${module_name}-system_tests: hightime diff --git a/generated/nidcpower/tox-system_tests.ini b/generated/nidcpower/tox-system_tests.ini index e0e466998b..ff8903bd31 100644 --- a/generated/nidcpower/tox-system_tests.ini +++ b/generated/nidcpower/tox-system_tests.ini @@ -32,7 +32,7 @@ commands = nidcpower-coverage: coverage report --rcfile=../../tools/coverage_system_tests.rc deps = - nidcpower-system_tests: pytest + nidcpower-system_tests: pytest<9.2.0 # Version capped until they fix https://github.com/pytest-dev/pytest/issues/15295 nidcpower-system_tests: coverage nidcpower-system_tests: numpy nidcpower-system_tests: hightime diff --git a/generated/nidigital/tox-system_tests.ini b/generated/nidigital/tox-system_tests.ini index 44541b14c7..f06bf6ebd9 100644 --- a/generated/nidigital/tox-system_tests.ini +++ b/generated/nidigital/tox-system_tests.ini @@ -40,7 +40,7 @@ deps = nidigital-wheel_dep: build nidigital-system_tests: nisync - nidigital-system_tests: pytest + nidigital-system_tests: pytest<9.2.0 # Version capped until they fix https://github.com/pytest-dev/pytest/issues/15295 nidigital-system_tests: coverage nidigital-system_tests: numpy nidigital-system_tests: hightime diff --git a/generated/nidmm/tox-system_tests.ini b/generated/nidmm/tox-system_tests.ini index 7d155899ac..3fe6493b80 100644 --- a/generated/nidmm/tox-system_tests.ini +++ b/generated/nidmm/tox-system_tests.ini @@ -32,7 +32,7 @@ commands = nidmm-coverage: coverage report --rcfile=../../tools/coverage_system_tests.rc deps = - nidmm-system_tests: pytest + nidmm-system_tests: pytest<9.2.0 # Version capped until they fix https://github.com/pytest-dev/pytest/issues/15295 nidmm-system_tests: coverage nidmm-system_tests: numpy nidmm-system_tests: hightime diff --git a/generated/nifake/tox-system_tests.ini b/generated/nifake/tox-system_tests.ini index f6cb55df65..b3cb7d8dd8 100644 --- a/generated/nifake/tox-system_tests.ini +++ b/generated/nifake/tox-system_tests.ini @@ -39,7 +39,7 @@ commands = deps = nifake-wheel_dep: build - nifake-system_tests: pytest + nifake-system_tests: pytest<9.2.0 # Version capped until they fix https://github.com/pytest-dev/pytest/issues/15295 nifake-system_tests: coverage nifake-system_tests: numpy nifake-system_tests: hightime diff --git a/generated/nifgen/tox-system_tests.ini b/generated/nifgen/tox-system_tests.ini index 3445883a2a..3d52e6c34c 100644 --- a/generated/nifgen/tox-system_tests.ini +++ b/generated/nifgen/tox-system_tests.ini @@ -39,7 +39,7 @@ commands = deps = nifgen-wheel_dep: build - nifgen-system_tests: pytest + nifgen-system_tests: pytest<9.2.0 # Version capped until they fix https://github.com/pytest-dev/pytest/issues/15295 nifgen-system_tests: coverage nifgen-system_tests: numpy nifgen-system_tests: hightime diff --git a/generated/nimodinst/tox-system_tests.ini b/generated/nimodinst/tox-system_tests.ini index 6ab4054491..272e3a4613 100644 --- a/generated/nimodinst/tox-system_tests.ini +++ b/generated/nimodinst/tox-system_tests.ini @@ -32,7 +32,7 @@ commands = nimodinst-coverage: coverage report --rcfile=../../tools/coverage_system_tests.rc deps = - nimodinst-system_tests: pytest + nimodinst-system_tests: pytest<9.2.0 # Version capped until they fix https://github.com/pytest-dev/pytest/issues/15295 nimodinst-system_tests: coverage nimodinst-system_tests: numpy nimodinst-system_tests: hightime diff --git a/generated/nirfsa/tox-system_tests.ini b/generated/nirfsa/tox-system_tests.ini index e93770db96..1cd1802184 100644 --- a/generated/nirfsa/tox-system_tests.ini +++ b/generated/nirfsa/tox-system_tests.ini @@ -39,7 +39,7 @@ commands = deps = nirfsa-wheel_dep: build - nirfsa-system_tests: pytest + nirfsa-system_tests: pytest<9.2.0 # Version capped until they fix https://github.com/pytest-dev/pytest/issues/15295 nirfsa-system_tests: coverage nirfsa-system_tests: numpy nirfsa-system_tests: hightime diff --git a/generated/nirfsg/tox-system_tests.ini b/generated/nirfsg/tox-system_tests.ini index 2d5c32524a..8486ec25c1 100644 --- a/generated/nirfsg/tox-system_tests.ini +++ b/generated/nirfsg/tox-system_tests.ini @@ -39,7 +39,7 @@ commands = deps = nirfsg-wheel_dep: build - nirfsg-system_tests: pytest + nirfsg-system_tests: pytest<9.2.0 # Version capped until they fix https://github.com/pytest-dev/pytest/issues/15295 nirfsg-system_tests: coverage nirfsg-system_tests: numpy nirfsg-system_tests: hightime diff --git a/generated/niscope/tox-system_tests.ini b/generated/niscope/tox-system_tests.ini index 2f232d27ab..8a64c8792b 100644 --- a/generated/niscope/tox-system_tests.ini +++ b/generated/niscope/tox-system_tests.ini @@ -39,7 +39,7 @@ commands = deps = niscope-wheel_dep: build - niscope-system_tests: pytest + niscope-system_tests: pytest<9.2.0 # Version capped until they fix https://github.com/pytest-dev/pytest/issues/15295 niscope-system_tests: coverage niscope-system_tests: numpy niscope-system_tests: hightime diff --git a/generated/nise/tox-system_tests.ini b/generated/nise/tox-system_tests.ini index 24128f4870..575af4e2a1 100644 --- a/generated/nise/tox-system_tests.ini +++ b/generated/nise/tox-system_tests.ini @@ -32,7 +32,7 @@ commands = nise-coverage: coverage report --rcfile=../../tools/coverage_system_tests.rc deps = - nise-system_tests: pytest + nise-system_tests: pytest<9.2.0 # Version capped until they fix https://github.com/pytest-dev/pytest/issues/15295 nise-system_tests: coverage nise-system_tests: numpy nise-system_tests: hightime diff --git a/generated/niswitch/tox-system_tests.ini b/generated/niswitch/tox-system_tests.ini index fba12360ae..bda523aaed 100644 --- a/generated/niswitch/tox-system_tests.ini +++ b/generated/niswitch/tox-system_tests.ini @@ -32,7 +32,7 @@ commands = niswitch-coverage: coverage report --rcfile=../../tools/coverage_system_tests.rc deps = - niswitch-system_tests: pytest + niswitch-system_tests: pytest<9.2.0 # Version capped until they fix https://github.com/pytest-dev/pytest/issues/15295 niswitch-system_tests: coverage niswitch-system_tests: numpy niswitch-system_tests: hightime diff --git a/generated/nitclk/tox-system_tests.ini b/generated/nitclk/tox-system_tests.ini index 26503d6ff4..8967f45923 100644 --- a/generated/nitclk/tox-system_tests.ini +++ b/generated/nitclk/tox-system_tests.ini @@ -39,7 +39,7 @@ commands = deps = nitclk-wheel_dep: build - nitclk-system_tests: pytest + nitclk-system_tests: pytest<9.2.0 # Version capped until they fix https://github.com/pytest-dev/pytest/issues/15295 nitclk-system_tests: coverage nitclk-system_tests: numpy nitclk-system_tests: hightime