Skip to content

Instantly share code, notes, and snippets.

@gvanem
Last active December 23, 2021 13:01
Show Gist options
  • Save gvanem/e36e1c6886f89fa9b39dd0c8655bcb09 to your computer and use it in GitHub Desktop.
Save gvanem/e36e1c6886f89fa9b39dd0c8655bcb09 to your computer and use it in GitHub Desktop.
GNU-makefile for the SDR-Radio program Quisk
#
# GNU Makefile for the SDR-Radio program Quisk.
#
# Supporting MinGW only. MSVC or clang-cl is not possible due
# to lack of C99 "complex types" support.
#
# By G. Vanem <[email protected]> 2020 - 2021.
#
# Ref: http://james.ahlstrom.name/quisk/
# https://pypi.org/project/quisk/#files
#
# You better do a 'make clean' after changing any of these
# settings or select another '$(CC)'. The only option now is
# CC=gcc (i.e. MinGW).
#
USE_AFEDRI ?= 1
USE_PERSEUS ?= 0
USE_PORTAUDIO ?= 1
USE_SOAPY ?= 1
USE_SDRIQ ?= 0
USE_WASAPI ?= 1
#
# Extract the version triplet from '__init__.py':
#
get_ver_digit = $(shell grep --only-matching '[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*' __init__.py | cut -d '.' -f$(1))
QUISK_MAJOR = $(call get_ver_digit,1)
QUISK_MINOR = $(call get_ver_digit,2)
QUISK_MICRO = $(call get_ver_digit,3)
VERSION = $(QUISK_MAJOR).$(QUISK_MINOR).$(QUISK_MICRO)
TODAY = $(shell date.exe +%d-%B-%Y)
VPATH = afedrinet \
perseuspkg \
sdriqpkg \
soapypkg
#
# We must use Python3.
# Use the Python launcher to start it.
#
PYTHON = py -3
PY_ROOT := $(subst \,/,$(shell $(PYTHON) -c "import sys; print(sys.prefix)"))
#
# Roots; change to suite:
#
LIBUSB_ROOT ?= $(realpath $(VCPKG_ROOT))/installed/x86-windows
FFTW_ROOT ?= $(realpath $(VCPKG_ROOT))/installed/x86-windows
PORTAUDIO_ROOT ?= $(realpath $(VCPKG_ROOT))/installed/x86-windows
PERSEUS_ROOT ?= ../libperseus_SDR
SOAPYSDR_ROOT ?= ../SoapySDR
WDSP_ROOT ?= ../WDSP
#
# Use Winsock-trace library, MSVC or clang-cl only.
#
USE_WSOCK_TRACE ?= 0
#
# For C-preprocessing.
#
USE_ASTYLE_FORMATTER ?= 1
USE_CLANG_FORMATER ?= 0
define Usage
Usage: "$(MAKE) -f Makefile.Windows CC=[gcc | cl | clang-cl] [all | clean | realclean]"
Specify CC=gcc for MinGW
Specify CC=cl for MSVC
Specify CC=clang-cl for clang-cl
endef
OBJ_DIR = objects
#
# We MUST use gcc due to use of "C99 complex" types.
#
CC = gcc
export CL=
ifeq ($(CC),cl)
O = obj
RCFLAGS = -D_MSC_VER
export CL=
else ifeq ($(CC),clang-cl)
O = obj
RCFLAGS = -D__clang__
else ifeq ($(CC),gcc)
O = o
RCFLAGS = -D__MINGW32__ --target=pe-i386 -O coff
else
$(error $(Usage))
endif
ifeq ($(CC),gcc)
CFLAGS = -m32 -O2 -fomit-frame-pointer
LDFLAGS = -m32
else
CFLAGS = -nologo -Zi -MD -Ot
LDFLAGS = -debug -map -verbose -nologo -incremental:no -nodefaultlib:uuid.lib
RCFLAGS = -nologo
endif
CFLAGS += -D_WIN32_WINNT=0x0601 \
-D_CRT_SECURE_NO_WARNINGS \
-D_CRT_NONSTDC_NO_WARNINGS \
-D_CRT_OBSOLETE_NO_WARNINGS \
-D_USE_MATH_DEFINES \
-DPy_ENABLE_SHARED \
-DFFTW_DLL \
-DQUISK_HAVE_DIRECTX \
-I. \
-I$(FFTW_ROOT)/include \
-I$(PY_ROOT)/include
CFLAGS += -DDEBUG_IO
#
# .DLLs that must be copied to 'py3_install' before running 'quisk.py'.
# Copy the .pdb files too in case some crashes.
#
COPY_DLLs = $(FFTW_ROOT)/bin/fftw3.dll \
$(FFTW_ROOT)/bin/fftw3.pdb \
$(WDSP_ROOT)/libwdsp.dll \
$(WDSP_ROOT)/libwdsp.pdb
#
# The Python3 C-modules. We always need this:
#
quisk_PYDs = py3_install/_quisk.pyd
#
# Untested by me.
#
ifeq ($(USE_SDRIQ),1)
quisk_PYDs += py3_install/sdriqpkg/sdriq.pyd
CFLAGS += -I$(xx_ROOT)
endif
#
# Works fine!
#
ifeq ($(USE_AFEDRI),1)
quisk_PYDs += py3_install/afedrinet/afedrinet_io.pyd
endif
#
# Not for Windows
#
ifeq ($(USE_PERSEUS),1)
quisk_PYDs += py3_install/afedrinet/perseus.pyd
COPY_DLLs += $(LIBUSB_ROOT)/bin/libusb-1.0.dll \
$(LIBUSB_ROOT)/bin/libusb-1.0.pdb
CFLAGS += -I$(PERSEUS_ROOT) \
-I$(LIBUSB_ROOT)/include \
-I$(OBJ_DIR)
GENERATED = $(OBJ_DIR)/libusb-1.0/libusb.h
endif
ifeq ($(USE_PORTAUDIO),1)
CFLAGS += -I$(PORTAUDIO_ROOT)/include -DQUISK_HAVE_PORTAUDIO
COPY_DLLs += $(PORTAUDIO_ROOT)/bin/portaudio.dll \
$(PORTAUDIO_ROOT)/bin/portaudio.pdb
PORTAUDIO_LIB = $(PORTAUDIO_ROOT)/lib/portaudio.lib
endif
ifeq ($(USE_SOAPY),1)
quisk_PYDs += py3_install/soapypkg/soapy.pyd
CFLAGS += -I$(SOAPYSDR_ROOT)/include
COPY_DLLs += $(SOAPYSDR_ROOT)/bin/SoapySDR.dll \
$(SOAPYSDR_ROOT)/bin/SoapySDR.pdb
endif
#
# Warning control.
#
ifeq ($(CC),clang-cl)
CFLAGS += -W4 \
-ferror-limit=5 \
-Wno-visibility \
# -Wno-unused-parameter \
# -Wno-missing-field-initializers \
# -Wno-incompatible-pointer-types
else ifeq ($(CC),cl)
CFLAGS += -W3
else
CFLAGS += -Wall
endif
ifeq ($(CC),gcc)
WS2_32 = -lws2_32
else ifeq ($(USE_WSOCK_TRACE),1)
WS2_32 = wsock_trace.lib
else
WS2_32 = ws2_32.lib
endif
#
# .c sources for 'py3_install/_quisk.pyd':
#
_quisk_SRC = filter.c \
extdemod.c \
freedv.c \
is_key_down.c \
microphone.c \
quisk.c \
quisk_wdsp.c \
sound.c \
sound_alsa.c \
sound_directx.c \
sound_portaudio.c \
sound_pulseaudio.c \
sound_wasapi.c \
utility.c
ifeq ($(USE_WASAPI),1)
CFLAGS += -DQUISK_HAVE_WASAPI
endif
_quisk_OBJ = $(addprefix $(OBJ_DIR)/, \
$(notdir $(_quisk_SRC:.c=.$(O))))
all: make_dirs \
$(GENERATED) \
$(quisk_PYDs) \
py3_install/run_quisk.bat \
copy_quisk_files \
epilogue
epilogue:
$(call yellow_msg, \nWelcome to Quisk ver $(VERSION).)
make_dirs::
- mkdir --parents $(OBJ_DIR) \
$(OBJ_DIR)/libusb-1.0 \
py3_install \
py3_install/afedrinet \
py3_install/soapypkg
.SECONDARY: $(OBJ_DIR)/afedrinet_io.rc \
$(OBJ_DIR)/perseus.rc \
$(OBJ_DIR)/sdriq.rc \
$(OBJ_DIR)/soapy.rc \
$(OBJ_DIR)/_quisk.rc
$(OBJ_DIR)/%.rc: Makefile.Windows
$(call Generating, $@, //)
$(file >> $@, #define RC_FILENAME "$(notdir $(@:.rc=.pyd))")
$(file >> $@, $(quisk_rc_common_stuff))
@echo
$(OBJ_DIR)/%.obj: %.c
$(CC) -c $(CFLAGS) -Fo$@ $<
@echo
$(OBJ_DIR)/%.o: %.c
$(CC) -c $(CFLAGS) -o $@ $<
@echo
$(OBJ_DIR)/libusb-1.0/libusb.h:
$(call Generating, $@, //)
$(file >> $@, #include <libusb/libusb.h>)
py3_install/run_quisk.bat: Makefile.Windows
$(call Generating, $@, ::)
$(file >> $@,$(run_quisk_bat))
%.i: %.c FORCE cpp_filter.py
$(call C_preprocess, $<, $@)
FORCE:
cpp_filter.py: Makefile.Windows
$(call green_msg, Generating $@)
$(file > $@,#!/usr/bin/env python)
$(file >> $@,from __future__ import print_function)
$(file >> $@,if 1:)
$(file >> $@,$(CPP_FILTER_PY))
$(OBJ_DIR)/%.obj: %.c
$(CC) -c $(CFLAGS) -Fo$@ $<
@echo
$(OBJ_DIR)/%.res: $(OBJ_DIR)/%.rc
$(call make_res, $@, $<)
#
# There is no 'make install'. So manually copy from './py3_install' to
# where-ever you like.
#
# Or make another .bat-file (alias, whatever) pointing to
# '%CD%\py3_install\run_quisk.bat'
#
quisk_FILES = defaults.html \
docs.html \
help.html \
help_conf.html \
help_vna.html \
configure.py \
dxcluster.py \
filters.py \
plot_wxmplot.py \
portaudio.py \
quisk.py \
quisk_conf_defaults.py \
quisk_conf_kx3.py \
quisk_conf_model.py \
quisk_conf_openradio.py \
quisk_conf_peaberry.py \
quisk_conf_sdr8600.py \
quisk_conf_sdriq.py \
quisk_conf_win.py \
quisk_hardware_fixed.py \
quisk_hardware_hamlib.py \
quisk_hardware_model.py \
quisk_hardware_sdr8600.py \
quisk_hardware_sdriq.py \
quisk_utils.py \
quisk_vna.py \
quisk_wdsp.py \
quisk_widgets.py \
softrock_tune_vfo.py \
__init__.py \
__main__.py
#
# Copy these to 'py3_install' also. Regardless of any 'USE_x' value.
#
afedrinet_FILES = afedrinet/*.py
soapypkg_FILES = soapypkg/*.py
freedvpkg_FILES = freedvpkg/*.py freedvpkg/libcodec2_32.dll
hermes_FILES = hermes/*.py
hiqsdr_FILES = hiqsdr/*.py
n2adr_FILES = n2adr/*.py
perseuspkg_FILES = perseuspkg/*.py
sdriqpkg_FILES = sdriqpkg/*.py
sdrmicronpkg_FILES = sdrmicronpkg/*.py
softrock_FILES = softrock/*.py
copy_quisk_files::
$(call copy_files, $(quisk_FILES), py3_install/)
$(call copy_files, $(COPY_DLLs), py3_install/)
$(call copy_files, $(afedrinet_FILES), py3_install/afedrinet/)
$(call copy_files, $(freedvpkg_FILES), py3_install/freedvpkg/)
$(call copy_files, $(hermes_FILES), py3_install/hermes/)
$(call copy_files, $(hiqsdr_FILES), py3_install/hiqsdr/)
$(call copy_files, $(n2adr_FILES), py3_install/n2adr/)
$(call copy_files, $(perseuspkg_FILES), py3_install/perseuspkg/)
$(call copy_files, $(sdriqpkg_FILES), py3_install/sdriqpkg/)
$(call copy_files, $(sdrmicronpkg_FILES), py3_install/sdrmicronpkg/)
$(call copy_files, $(soapypkg_FILES), py3_install/soapypkg/)
$(call copy_files, $(softrock_FILES), py3_install/softrock/)
#
# Hopefully there is only 1 Python3.x library for MinGW matching this.
# Get the first match.
#
ifeq ($(CC),gcc)
PY_LIB = $(word 1, $(wildcard $(PY_ROOT)/libs/libpython3*.a))
else
PY_LIB = $(PY_ROOT)/libs/python3.lib
endif
#
# External lib(s) to use for '_quisk.pyd':
# The MSVC-built import libraries for FFTW and PortAudio should work with MinGW too.
#
_quisk_LIBS = $(PY_LIB) $(FFTW_ROOT)/lib/fftw3.lib $(PORTAUDIO_LIB) $(WS2_32)
ifeq ($(CC),gcc)
_quisk_LIBS += -liphlpapi -lole32 -lwinmm -lavrt -ldsound
else
_quisk_LIBS += iphlpapi.lib ole32.lib winmm.lib avrt.lib dsound.lib
endif
py3_install/_quisk.pyd: $(_quisk_OBJ) $(OBJ_DIR)/_quisk.res
$(call link_PYD, $@, $(_quisk_OBJ) $(OBJ_DIR)/_quisk.res $(_quisk_LIBS))
py3_install/afedrinet/perseus.pyd: \
$(OBJ_DIR)/perseus.$(O) \
$(OBJ_DIR)/import_quisk_api.$(O) \
$(OBJ_DIR)/perseus.res \
$(PY_LIB)
$(call link_PYD, $@, $^)
py3_install/sdriqpkg/sdriq.pyd: \
$(OBJ_DIR)/sdriq.$(O) \
$(OBJ_DIR)/import_quisk_api.$(O) \
$(OBJ_DIR)/sdriq.res \
$(PY_LIB)
$(call link_PYD, $@, $^)
py3_install/soapypkg/soapy.pyd: \
$(OBJ_DIR)/soapy.$(O) \
$(OBJ_DIR)/import_quisk_api.$(O) \
$(OBJ_DIR)/soapy.res \
$(SOAPYSDR_ROOT)/lib/SoapySDR.lib \
$(PY_LIB)
$(call link_PYD, $@, $^)
py3_install/afedrinet/afedrinet_io.pyd: \
$(OBJ_DIR)/afedrinet_io.$(O) \
$(OBJ_DIR)/import_quisk_api.$(O) \
$(OBJ_DIR)/is_key_down.$(O) \
$(OBJ_DIR)/afedrinet_io.res \
$(PY_LIB)
$(call link_PYD, $@, $^ $(WS2_32))
clean:
rm -f link.tmp cpp_filter.py .depend.Windows
rm -fr $(OBJ_DIR)
vclean: clean
rm -fR py3_install/*
- rmdir py3_install
#
# GNU-make macro:
#
# The following ESC codes assumes you have a MSys/Cygwin 'echo.exe' program with colour support.
#
BRIGHT_GREEN = \e[1;32m
BRIGHT_YELLOW = \e[1;33m
BRIGHT_WHITE = \e[1;37m
colour_msg = @echo -e "$(1)\e[0m"
green_msg = $(call colour_msg,$(BRIGHT_GREEN)$(strip $(1)))
yellow_msg = $(call colour_msg,$(BRIGHT_YELLOW)$(strip $(1)))
#
# Note:
# $(empty) is not a space. It marks a boundary for something else.
# So '$(empty) $(empty)' becomes a single space.
#
empty :=
space := $(empty) $(empty)
define Warning
$(1)
$(1) DO NOT EDIT! This file was automatically generated
$(1) from $(realpath Makefile.Windows) at $(TODAY).
$(1) Edit that file instead.
$(1)
endef
define Generating
$(call green_msg, Generating $(1))
$(file > $(1),$(call Warning,$(strip $(2))))
endef
#
# .$(O) -> .pyd macro
# arg1, $(1): The .pyd file.
# arg2, $(2): The .$(O) file(s), extra options and libs.
#
define link_PYD
$(call green_msg, Linking $@)
$(call link_PYD_$(CC), $(1), $(2))
@echo
endef
define link_PYD_cl
link $(LDFLAGS) -libpath:$(PY_ROOT)/libs -dll -out:$(strip $(1)) $(2) > link.tmp
@cat link.tmp >> $(1:.pyd=.map)
@rm -f $(1:.pyd=.lib) $(1:.pyd=.exp)
endef
link_PYD_gcc = gcc $(LDFLAGS) -L$(PY_ROOT)/libs -shared -o $(1) $(2)
link_PYD_clang-cl = $(call link_PYD_cl, $(1), $(2))
define make_res
$(call make_res_$(CC), $(1), $(2))
@echo
endef
make_res_cl = rc $(RCFLAGS) -Fo $(1) $(2)
make_res_clang-cl = $(call make_res_cl, $(1), $(2))
make_res_gcc = windres $(RCFLAGS) -o $(1) -i $(2)
ifeq ($(USE_ASTYLE_FORMATTER),1)
C_FORMATER = | astyle
else ifeq ($(USE_CLANG_FORMATER),1)
C_FORMATER = | clang-format -style=Mozilla -assume-filename=c
endif
define C_preprocess
$(file > $(2),/* The preprocessed output of '$(strip $(1))':)
$(file >> $(2), * $(CC) -E)
@$(foreach f, $(CFLAGS), $(file >> $(2), * $(f)))
$(file >> $(2), *---------------------------------------------------------)
$(file >> $(2), */)
$(CC) -E $(CFLAGS) $(1) | $(PYTHON) cpp_filter.py $(C_FORMATER) >> $(2)
endef
#
# copy files macro:
# $(1): files. A '$(sort $(1))' will make a unique list.
# $(2): directory
#
define copy_files
$(call green_msg, Updating these files into $(BRIGHT_WHITE)$(strip $(2)):)
@$(foreach f, $(1), echo '$(space) $(f)' ; )
- @mkdir --parents $(2)
@cp --update $(sort $(1)) $(2)
@echo
endef
#
# The contents of 'py3_install/run_quisk.bat':
#
define run_quisk_bat
@echo off
setlocal
set WSOCK_TRACE_LEVEL=0
cd /D %~dp0
:: py.exe should normally be here: |
:: v
:: start "Quisk SDR" /min %SystemRoot%\py.exe -3 quisk.py %*
start "Quisk SDR" /min $(subst /,\,$(PYTHON)) quisk.py %*
endef
define quisk_rc_common_stuff
#include <winver.h>
#define VER_STRING "$(QUISK_MAJOR).$(QUISK_MINOR).$(QUISK_MICRO)"
#define RC_VERSION $(QUISK_MAJOR),$(QUISK_MINOR),$(QUISK_MICRO),0
#ifndef RC_FILENAME
#error "Add a 'RC_FILENAME' first"
#endif
#if defined(__MINGW32__)
#define CC_STRING "MinGW32"
#elif defined(__clang__)
#define CC_STRING "clang-cl"
#else
#define CC_STRING "MSVC"
#endif
VS_VERSION_INFO VERSIONINFO
FILEVERSION RC_VERSION
PRODUCTVERSION RC_VERSION
FILEFLAGSMASK 0x3fL
FILEOS 0x40004L
FILETYPE 0x2L
FILESUBTYPE 0x0L
FILEFLAGS 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "http://james.ahlstrom.name/quisk/"
VALUE "FileDescription", "Python3 module for Quisk, a Software Defined Radio (SDR) transceiver."
VALUE "FileVersion", VER_STRING
VALUE "InternalName", "Quisk"
VALUE "LegalCopyright", "GNU General Public License (GPL)"
VALUE "LegalTrademarks", "http://www.gnu.org/licenses/"
VALUE "OriginalFilename", RC_FILENAME
VALUE "ProductName", RC_FILENAME
VALUE "ProductVersion", VER_STRING " (" CC_STRING ", x86, release)"
VALUE "PrivateBuild", "The private build of <[email protected]>"
VALUE "SpecialBuild", ""
VALUE "Comments", "Built at $(TODAY)"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
endef
define CPP_FILTER_PY
import sys, os
try:
import ntpath
except ImportError as e:
print ("Failed to import ntpath: %s" % e)
sys.exit(1)
def _win32_abspath (path):
path = ntpath.abspath (path)
return path.replace ('\\', '/')
cwd = _win32_abspath (os.getcwd()) + '/'
last_line = '??'
last_fname = '??'
empty_lines = 0
while True:
line = sys.stdin.readline()
if not line:
break
if line.startswith('\n') or line.startswith('\r'):
empty_lines += 1
continue
#
# Strip off MSVC or 'gcc -E ...' line-numbered lines
#
line_numbered = line.startswith ("#line ") or line.startswith ("# ")
if line_numbered:
line = line.replace ("\\\\", "/")
fname = None
quote = line.find ('\"')
if line_numbered and quote > 0:
fname = _win32_abspath (line[quote:])
last_fname = fname
if line.strip() != '' and last_line != '':
if fname is None or fname != last_fname:
print (line, end="")
if line.strip() == '}' or line.strip() == '};': # Print a newline after a functions or structs
print ("")
last_line = line
if empty_lines > 0:
sys.stderr.write ("Removed %d empty lines.\n" % empty_lines)
endef
DEP_CFLAGS = -MM $(filter -D% -I%, $(CFLAGS))
DEP_REPLACE = sed -e 's@\(.*\)\.o: @\n$$(OBJ_DIR)\/\1.$$(O): @' \
-e 's@$(PY_ROOT)@$$(PY_ROOT)@'
depend:
$(call Generating, .depend.Windows, #)
gcc $(DEP_CFLAGS) $(_quisk_SRC) | $(DEP_REPLACE) >> .depend.Windows
-include .depend.Windows
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment