Skip to content

Instantly share code, notes, and snippets.

@gvanem
Last active February 7, 2024 09:28
Show Gist options
  • Select an option

  • Save gvanem/4c5622bbbaa8cbf1da6c55f1e9d2f831 to your computer and use it in GitHub Desktop.

Select an option

Save gvanem/4c5622bbbaa8cbf1da6c55f1e9d2f831 to your computer and use it in GitHub Desktop.
A GNU-makefile for Pycurl/MSVC (tired of 'setup.py build' problems on Windows). Put in 'src' directory of Pycurl and do 'make -f Makefile.MSVC clean install'
#
# GNU makefile 4.x for pycurl (MSVC)
#
# By <[email protected]> 2013 - 2024.
#
THIS_FILE := $(firstword $(MAKEFILE_LIST))
DATE := $(shell date +%d-%B-%Y)
YEAR := $(shell date +%Y)
VERSION := 7.45.2
MODULE = pycurl
#
# Options: Change to suite.
#
# Use Python 3.x (=1) or Python 2.x (=0)
# Can be used from the command-line too:
# make -f Makefile.MSVC clean all USE_PY3=0
#
# The bitness (x86/x64) of Python X must off-cource match the bitness
# of libcurl / OpenSSL libs.
#
USE_PY3 ?= 1
USE_OPENSSL ?= 1
USE_WINSSL ?= 1
USE_WSOCK_TRACE ?= 1
#
# Change to suite:
#
CURL_ROOT ?= ../..
OPENSSL_ROOT ?= ../../../Crypto/OpenSSL
#
# Change to suite. These are my .lib names:
#
CURL_LIB ?= $(CURL_ROOT)/lib/libcurl_imp-$(CPU).lib
OPENSSL_LIBS ?= $(OPENSSL_ROOT)/lib/libcrypto_imp-$(CPU).lib \
$(OPENSSL_ROOT)/lib/libssl_imp-$(CPU).lib
PY2_ROOT = $(subst \,/,$(shell py.exe -2 -c 'import sys; print(sys.prefix)'))
PY3_ROOT = $(subst \,/,$(shell py.exe -3 -c 'import sys; print(sys.prefix)'))
OBJ_DIR = objects
#
# This assumes you have at least one 'python.exe' on PATH found by
# the Python launcher 'py.exe'. Usually 'c:\Windows\py.exe'.
#
# The 'LIB' env-var should already be setup to point to the location
# of 'python*.lib'. But specify it anyway explicitly in '$(EX_LIBS)' below.
#
ifeq ($(USE_PY3),1)
PYTHON_ROOT = $(PY3_ROOT)
PYTHON_LIB = $(word 1, $(wildcard $(PYTHON_ROOT)/libs/python3*.lib))
PY_MAJOR = 3
else
PYTHON_ROOT = $(PY2_ROOT)
PYTHON_LIB = $(word 1, $(wildcard $(PYTHON_ROOT)/libs/python2*.lib))
PY_MAJOR = 2
endif
PYTHON = py.exe -$(PY_MAJOR)
PY_MINOR = $(shell $(PYTHON) -c 'import sys; print (sys.version_info.minor)')
PY_VERSION = $(PY_MAJOR).$(PY_MINOR)
#
# Python prior to 3.4 prints version-info to 'stderr'.
# Python after 3.4 prints version-info to 'stdout'.
# Hence this method is unrealiable:
#
# PY_VERSION = $(shell $(PYTHON) -V 2>&1 | sed -e 's/Python //' -e 's/\.[0-9]$$//')
#
CC = cl
CFLAGS = -nologo -MD -Zi -Ot -W3 -GF \
-I. \
-I$(PYTHON_ROOT)/include \
-I$(CURL_ROOT)/include \
-I./$(OBJ_DIR) \
-DWIN32_LEAN_AND_MEAN \
-D_WIN32_WINNT=0x0601 \
-DPYCURL_INTERNAL= \
-DPYCURL_AVOID_STDIO \
-DPYCURL_VERSION='$(VERSION)' \
# -D_WINSOCK_DEPRECATED_NO_WARNINGS
LDFLAGS = -nologo -debug -verbose -map -incremental:no -dll -manifest -libpath:$(PYTHON_ROOT)/libs
RCFLAGS = -nologo -r -I$(PYTHON_ROOT)/include
ifeq ($(USE_PY3),1)
LDFLAGS += -export:PyInit_pycurl
else
LDFLAGS += -export:initpycurl
endif
EX_LIBS = $(PYTHON_LIB) $(CURL_LIB)
ifneq ($(USE_OPENSSL)$(USE_WINSSL),00)
# $(error "Cannot have both 'USE_OPENSSL=1' and 'USE_WINSSL=1'.")
CFLAGS += -DHAVE_CURL_SSL
endif
ifeq ($(USE_OPENSSL),1)
CFLAGS += -I$(OPENSSL_ROOT)/include -DHAVE_CURL_OPENSSL
EX_LIBS += $(OPENSSL_LIBS)
endif
ifeq ($(USE_WINSSL),1)
CFLAGS += -DHAVE_CURL_WINSSL -DHAVE_CURL_SCHANNEL
endif
#
# A 'wsock_trace-x64.lib' must be on 'LIB' path.
#
ifeq ($(USE_WSOCK_TRACE),1)
OS_LIBS = wsock_trace-$(CPU).lib
else
OS_LIBS = ws2_32.lib
endif
OS_LIBS += kernel32.lib
GENERATED = $(OBJ_DIR)/docstrings.c \
$(OBJ_DIR)/docstrings.h \
$(OBJ_DIR)/manifest.c
SOURCES = easy.c \
easycb.c \
easyinfo.c \
easyopt.c \
easyperform.c \
module.c \
multi.c \
oscompat.c \
pythoncompat.c \
share.c \
stringcompat.c \
threadsupport.c \
util.c
vpath %.c $(OBJ_DIR)
SOURCES += $(OBJ_DIR)/docstrings.c \
$(OBJ_DIR)/manifest.c
OBJECTS = $(addprefix $(OBJ_DIR)/, \
$(notdir $(SOURCES:.c=.obj)))
#################################################################################
all: intro $(GENERATED) $(MODULE).pyd epilogue
intro:
ifeq ($(PYTHON_LIB),)
$(error 'PYTHON_LIB' not found.)
endif
$(call green_msg, Building pycurl v. $(VERSION) using Python ver. $(PY_VERSION).)
epilogue:
$(call green_msg, Do a "make -f $(THIS_FILE) install" at own risk.)
$(MODULE).pdb: $(MODULE).pyd
$(MODULE).pyd: $(OBJECTS) $(OBJ_DIR)/$(MODULE).res $(EX_LIBS)
link $(LDFLAGS) -out:$@ $^ $(OS_LIBS) > link.tmp
@cat link.tmp >> $(MODULE).map
mt -nologo -manifest [email protected] -outputresource:"$@;1"
@rm -f $(MODULE).{exp,lib}
@echo
$(OBJ_DIR):
mkdir $@
#
# Just some test targets
#
get_manifest: $(MODULE).pyd
mt -nologo -inputresource:$(MODULE).pyd -out:manifest.txt
@echo
test:
$(call green_msg0, PY2_ROOT: "$(PY2_ROOT)".)
$(call green_msg0, PY3_ROOT: "$(PY3_ROOT)".)
$(call green_msg0, VERSION: "$(VERSION)".)
$(OBJ_DIR)/%.obj: %.c | $(OBJ_DIR)
$(CC) -c $(CFLAGS) -Fo$@ $<
@echo
$(OBJ_DIR)/$(MODULE).res: $(OBJ_DIR)/$(MODULE).rc
rc $(RCFLAGS) -fo $@ $<
@echo
$(OBJ_DIR)/$(MODULE).rc: $(THIS_FILE)
$(call generate, $@, //)
$(file >> $@,$(PYCURL_RC))
$(OBJ_DIR)/manifest.c: $(THIS_FILE) | $(OBJ_DIR)
$(call generate, $@, //)
$(file > $@,$(MANIFEST_C))
$(OBJ_DIR)/docstrings.c $(OBJ_DIR)/docstrings.h: | $(OBJ_DIR)
cd .. ; \
$(PYTHON) setup.py docstrings
mv docstrings.[ch] $(OBJ_DIR)
@echo
install: $(MODULE).pyd $(MODULE).pdb ../python/curl/__init__.py
- mkdir $(PYTHON_ROOT)/Lib/site-packages/curl
cp --update $(MODULE).pyd $(MODULE).pdb $(PYTHON_ROOT)/Lib/site-packages
cp --update ../python/curl/__init__.py $(PYTHON_ROOT)/Lib/site-packages/curl
@echo
clean:
rm -fr $(OBJ_DIR)
rm -f vc1*.pdb link.tmp $(MODULE).pyd.manifest
vclean realclean: clean
rm -f $(MODULE).{pyd,pdb,lib,map} .depend.MSVC
comma := ,
$(OBJ_DIR)/cpp-filter.py: $(THIS_FILE) | $(OBJ_DIR)
$(call generate, $@, #)
$(file >> $@,if 1:)
$(file >> $@,$(CPP_FILTER_PY))
#
# To generated pre-processed output (in case something goes wrong).
#
%.i: %.c FORCE | $(OBJ_DIR)/cpp-filter.py
$(CC) -E $(CFLAGS) $< | $(PYTHON) $(OBJ_DIR)/cpp-filter.py > $@
@echo
FORCE:
DEP_CFLAGS = -MM $(filter -D% -I%, $(CFLAGS))
DEP_REPLACE = sed -e 's/\(.*\)\.o: /\n\1.obj: /'
#
# Since this could be a Cygwin 'gcc':
#
DEP_CFLAGS += -DPy_PYPORT_H -DPYLONG_BITS_IN_DIGIT=30 -D_MSC_VER=1900
depend: $(THIS_FILE) $(GENERATED)
gcc $(DEP_CFLAGS) $(SOURCES) | $(DEP_REPLACE) > .depend.MSVC
@echo
#
# GNU-make macros.
#
# This assumes you have a MSys/Cygwin's 'echo.exe' with colour support.
#
BRIGHT_GREEN = \e[1;32m
colour_msg = @echo -e '$(1)\e[0m'
green_msg = $(call colour_msg,$(BRIGHT_GREEN)$(strip $(1)))
green_msg0 = $(call colour_msg,$(BRIGHT_GREEN)$(1))
define generate
$(call green_msg, Generating $(1))
$(file > $(1),$(call Warning,$(2)))
endef
define Warning
$(1)
$(1) DO NOT EDIT! This file was automatically generated
$(1) from $(realpath $(THIS_FILE)) at $(DATE).
$(1) Edit that file instead.
$(1)
endef
define PYCURL_RC
#include <winver.h>
#include "patchlevel.h" /* In '$(PYTHON_ROOT)/include' */
#define RC_VERSION $(subst .,$(comma),$(VERSION))
#define RC_VER_STRING "$(VERSION)"
#define PY_VER_STRING "$(PY_VERSION)"
APPICON ICON "../doc/static/favicon.ico"
VS_VERSION_INFO VERSIONINFO
FILEVERSION RC_VERSION
PRODUCTVERSION RC_VERSION
FILEFLAGSMASK 0x3FL
FILEFLAGS 0
FILEOS VOS__WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904B0"
BEGIN
VALUE "CompanyName", "Python interface to libcurl."
VALUE "FileDescription", "Python interface to libcurl."
VALUE "FileVersion", RC_VER_STRING ", Python " PY_VER_STRING "."
VALUE "InternalName", "PycURL"
VALUE "OriginalFilename", "$(MODULE).pyd."
VALUE "ProductName", "Python$(PY_MAJOR) interface to libcurl."
VALUE "ProductVersion", RC_VER_STRING " (MSVC) built on $(DATE)."
VALUE "Comments", ""
VALUE "LegalCopyright", "LGPL and an MIT/X derivative license.\r\n"
"\t\t\tCopyright (C) 2001-2008 Kjetil Jacobsen.\r\n"
"\t\t\tCopyright (C) 2001-2008 Markus F.X.J. Oberhumer."
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
#define MANIFEST_RESOURCE_ID 1
#define RT_MANIFEST 24
#if 0
/*
* How to solve this problem: The below manifest is generated in the
* link step. The link step also needs the .res file. Not sure a
* manifest in a .res-file is a good idea anyway.
*/
MANIFEST_RESOURCE_ID RT_MANIFEST "$(MODULE).pyd.manifest"
#endif
endef
define MANIFEST_C
#pragma comment (linker, "/manifestdependency:type='win32'")
#pragma comment (linker, "/manifestdependency:name='Microsoft.VC90.CRT'")
#pragma comment (linker, "/manifestdependency:version='9.0.30411.0'")
#pragma comment (linker, "/manifestdependency:processorArchitecture='$(PROCESSOR_ARCHITECTURE)'")
#pragma comment (linker, "/manifestdependency:publicKeyToken='1fc8b3b9a1e18e3b'")
endef
define CPP_FILTER_PY
import sys, os
empty_lines = 0
while True:
line = sys.stdin.readline()
if not line:
break
line = line.rstrip()
if line == "":
empty_lines += 1
continue
if line.lstrip().startswith("#line") or line.lstrip().startswith("# "):
line = line.replace (r"\\", "/")
print (line)
if line == "}" or line == "};":
print ("")
print ("Removed %d empty lines." % empty_lines, file=sys.stderr)
endef
-include .depend.MSVC
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment