Skip to content

Instantly share code, notes, and snippets.

@gvanem
Created October 16, 2025 05:28
Show Gist options
  • Select an option

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

Select an option

Save gvanem/d03dc7e49d87cf4290a9dbb811ee34c7 to your computer and use it in GitHub Desktop.
Makefile for NGrep
#
# ngrep Makefile for Windows; MSVC + clang-cl
#
# Ref: https://github.com/jpr5/ngrep.git
#
THIS_FILE := Makefile.Windows
TODAY := $(shell date +%d-%B-%Y)
PYTHON := py -3
MAKEFLAGS += --warn-undefined-variables
VCPKG_ROOT := $(realpath $(VCPKG_ROOT))
PCRE2_ROOT ?= $(VCPKG_ROOT)/installed/$(CPU)-windows-static
PCAP_ROOT ?= f:/MinGW32/src/inet/libpcap
NPCAP_ROOT ?= f:/MinGW32/src/inet/libpcap/NPcap
LIBNET_ROOT ?= f:/MinGW32/src/inet/libnet
INSTALL_ROOT := $(realpath $(VSINSTALLDIR))
MANPAGE_ROOT ?= f:/MinGW32/share/man
USE_ASTYLE ?= 1
USE_PCRE2 ?= 1
USE_STATIC_PCAP ?= 0
USE_TCPKILL ?= 0
USE_TRACE ?= 0
USE_WSOCK_TRACE ?= 1
#
# From 'ngrep.h':
#
VER_MAJOR = 1
VER_MINOR = 48
VER_MICRO = 0
VERSION = $(VER_MAJOR).$(VER_MINOR).$(VER_MICRO)
define Usage
Usage: "make -f $(THIS_FILE) CC=[cl | clang-cl] [all | clean | vclean | realclean | install]"
Specify CC=cl - use MSVC
Specify CC=clang-cl - use clang-cl
endef
#
# Undefine these:
#
export CL=
export C_INCLUDE_PATH=
OBJ_DIR = objects
CFLAGS = -nologo -MD -W3 -Zi \
-GS- -Gy -GF -Ob2 \
-Ox -Oy -Ot \
-I. \
-I./winXX/support \
-I./regex-0.12 \
-I./$(OBJ_DIR) \
-I$(PCAP_ROOT) \
-Fd./$(OBJ_DIR)/vc14x.pdb
LDFLAGS = -nologo -verbose -incremental:no -debug -nodefaultlib:uuid.lib
RCFLAGS = -nologo
ifeq ($(CC),cl)
RCFLAGS += -D_MSC_VER
else ifeq ($(CC),clang-cl)
RCFLAGS += -D__clang__
CFLAGS += -fms-compatibility \
-ferror-limit=5
else
$(error $(Usage))
endif
ifeq ($(USE_WSOCK_TRACE),1)
WS2_32_LIB = wsock_trace-$(CPU).lib
LDFLAGS += -nodefaultlib:ws2_32.lib
else
WS2_32_LIB = ws2_32.lib
endif
#
# External libraries needed:
#
EX_LIBS =
OS_LIBS = $(WS2_32_LIB)
#
# Make 'regex.c' shut-up about 'alloca()'.
#
CFLAGS += -FImalloc.h
#
# Do not use 'winXX/support/config.h'
#
CFLAGS += -Dwin32_CONFIG_H
#
# But this instead:
#
CFLAGS += -FI./$(OBJ_DIR)/config.h
ifeq ($(USE_STATIC_PCAP),1)
EX_LIBS += $(PCAP_ROOT)/lib/wpcap2_static-$(CPU).lib
OS_LIBS += advapi32.lib iphlpapi.lib ntdll.lib shell32.lib version.lib winmm.lib
else
CFLAGS += -DPCAP_DLL
EX_LIBS += $(PCAP_ROOT)/lib/wpcap-$(CPU).lib
endif
ifeq ($(USE_PCRE2),1)
CFLAGS += -DUSE_PCRE2=1 -I$(PCRE2_ROOT)/include
LDFLAGS += -nodefaultlib:libcmt.lib
EX_LIBS += $(PCRE2_ROOT)/lib/pcre2-8.lib
else
CFLAGS += -DUSE_PCRE2=0
endif
vpath %.c regex-0.12 \
winXX/support
SOURCES = ngrep.c \
regex-0.12/regex.c \
winXX/support/getopt.c
ifeq ($(USE_TCPKILL),1)
CFLAGS += -DUSE_TCPKILL=1 \
-I$(LIBNET_ROOT)/include \
-I$(NPCAP_ROOT)/Common
SOURCES += tcpkill.c
EX_LIBS += $(LIBNET_ROOT)/lib/libnet_imp-$(CPU).lib
endif
ifeq ($(USE_TRACE),1)
CFLAGS += -DUSE_TRACE
SOURCES += trace.c
endif
OBJECTS = $(addprefix $(OBJ_DIR)/, \
$(notdir $(SOURCES:.c=.obj)))
GENERATED = $(addprefix $(OBJ_DIR)/, \
config.h \
err.h \
unistd.h)
all: $(GENERATED) bin/ngrep.exe epilogue
epilogue:
$(call green_msg, Welcome to ngrep/Win32 $(BRIGHT_WHITE)(VERSION=$(VERSION), CC=$(CC)))
$(OBJ_DIR) bin:
mkdir $@
bin/ngrep.exe: $(OBJECTS) $(EX_LIBS) $(OBJ_DIR)/ngrep.res | bin
$(call link_EXE, $@, $^ $(OS_LIBS))
$(OBJ_DIR)/%.obj: %.c | $(GENERATED)
$(call C_compile, $@, $<)
$(OBJ_DIR)/%.res: $(OBJ_DIR)/%.rc
rc $(RCFLAGS) -fo./$@ $<
@echo
$(OBJ_DIR)/config.h: $(THIS_FILE) | $(OBJ_DIR)
$(call generate, $@, //)
$(file >> $@, $(NGREP_CONFIG_H))
$(OBJ_DIR)/err.h: $(THIS_FILE) | $(OBJ_DIR)
$(call generate, $@, //)
$(file >> $@, $(ERR_H))
$(OBJ_DIR)/unistd.h: $(THIS_FILE) | $(OBJ_DIR)
$(call generate, $@, //)
$(file >> $@, // Fake <unistd.h> for Windows)
$(OBJ_DIR)/ngrep.rc: $(THIS_FILE) | $(OBJ_DIR)
$(call generate, $@, //)
$(file >> $@, $(NGREP_RC))
%.i: %.c FORCE | $(GENERATED) $(OBJ_DIR)/cpp-filter.py
$(call C_preprocess, $@, $<)
FORCE:
$(OBJ_DIR)/cpp-filter.py: $(THIS_FILE) | $(OBJ_DIR)
$(call generate, $@, #)
$(file >> $@,if 1:)
$(file >> $@,$(CPP_FILTER_PY))
clean:
rm -fr $(OBJ_DIR)
rm -f link.tmp
vclean realclean: clean
rm -fr bin
install: all
cp --update bin/ngrep.exe bin/ngrep.pdb $(INSTALL_ROOT)/bin
cp --update ngrep.8 $(MANPAGE_ROOT)/man8
#
# GNU-make macros
#
BRIGHT_GREEN = \e[1;32m
BRIGHT_WHITE = \e[1;37m
colour_msg = @echo -e "$(1)\e[0m"
green_msg = $(call colour_msg,$(BRIGHT_GREEN)$(strip $(1)))
define C_compile
$(CC) -c $(CFLAGS) -Fo./$(strip $(1) $(2))
@echo
endef
pp_filter = $(PYTHON) $(OBJ_DIR)/cpp-filter.py
ifeq ($(USE_ASTYLE),1)
pp_comment = The preprocessed and Astyled output of '$(strip $(1))'
pp_filter += | astyle
else
pp_comment = The raw preprocessed output of '$(strip $(1))'
endif
define C_preprocess
$(file > $(1),/* $(call pp_comment, $(2)):)
$(file >> $(1), * $(CC) -E)
$(foreach f, $(CFLAGS), $(file >> $(1), * $(f)))
$(file >> $(1), *)
$(file >> $(1), *---------------------------------------------------------)
$(file >> $(1), */)
$(CC) -E $(CFLAGS) $(2) | $(pp_filter) >> $(1)
endef
define generate
$(call green_msg, Generating $(BRIGHT_WHITE)$(strip $(1)))
$(file > $(1), $(2))
$(file > $(1), $(2) This file was generated by $(THIS_FILE) at $(TODAY).)
$(file > $(1), $(2) DO NOT EDIT. YOUR CHANGED WILL BE LOST.)
$(file > $(1), $(2))
endef
define link_EXE
$(file > $(OBJ_DIR)/check-for-unused-libs.py,if 1:)
$(file >> $(OBJ_DIR)/check-for-unused-libs.py,$(CHECK_FOR_UNUSED_LIBS_PY))
$(call green_msg, Linking $(BRIGHT_WHITE)$(strip $(1)))
link -out:$(strip $(1)) $(LDFLAGS) $(2) > link.tmp
@cat link.tmp >> $(1:.exe=.map)
@rm -f $(1:.exe=.lib) $(1:.exe=.exp)
@$(PYTHON) $(OBJ_DIR)/check-for-unused-libs.py link.tmp
endef
define NGREP_CONFIG_H
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#define _CRT_NONSTDC_NO_WARNINGS
#define _CRT_SECURE_NO_DEPRECATE
/*
* Warning control:
*/
#if defined(__clang__)
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#pragma clang diagnostic ignored "-Wincompatible-pointer-types"
#pragma clang diagnostic ignored "-Wnon-literal-null-conversion"
#pragma clang diagnostic ignored "-Wunused-variable"
#pragma clang diagnostic ignored "-Wunused-but-set-variable"
#pragma clang diagnostic ignored "-Wdangling-else"
#pragma clang diagnostic ignored "-Wvisibility"
#pragma clang diagnostic ignored "-Wpointer-to-int-cast"
#pragma clang diagnostic ignored "-Wint-to-pointer-cast"
#pragma clang diagnostic ignored "-Wdeprecated-non-prototype"
#elif defined(_MSC_VER)
#pragma warning (disable: 4018 4047 4244 4267 4311 4312 4996)
#endif
/*
* Just a test.
*/
#define USE_VLAN_HACK 1
/*
* For 'regex-0.12/regex.c':
*/
#define HAVE_STRING_H 1
#define STDC_HEADERS 1
/*
* For Winsock and 'inet_ntop()'
*/
#define _WIN32_WINNT 0x0601
#define USE_IPv6 1
#ifdef USE_TCPKILL
/*
* Ignore '$$(NPCAP_ROOT)/Common/npcap-bpf.h'
*/
#define NPCAP_BPF_H
#endif
/* Instead of patching 'ngrep.c' with '#ifdef DLT_RAW' etc.
* just do this. A value '0' or '1' isn't important.
* Only '0' or '>0' is.
*
* BUT, how the heck can we get a 'DLT_LINUX_SLL' from 'pcap_datalink()'
* on Win32 anyway? AFAICS, the 3 first of these 'HAVE_x' should always
* be set to '0'.
*/
#define HAVE_DLT_RAW (0+DLT_RAW)
#define HAVE_DLT_LOOP (0+DLT_LOOP)
#define HAVE_DLT_LINUX_SLL (0+DLT_LINUX_SLL)
#define HAVE_DLT_IEEE802_11 (0+DLT_IEEE802_11)
#define HAVE_DLT_IEEE802_11_RADIO (0+DLT_IEEE802_11_RADIO)
#define USE_PCAP_RESTART 0
#define PCAP_RESTART_FUNC abort /* just in case some fubar is going on */
#if defined(USE_TRACE)
/*
* Trace printer controlled by env-var "set NGREP_TRACE=level":
* Ref. 'trace.c'
*/
#define TRACE(level, fmt, ...) \
do { \
if (trace_level() >= level) \
trace_printf (__FILE__, __LINE__, fmt, ## __VA_ARGS__); \
} while (0)
int trace_level (void);
void trace_printf (const char *file, unsigned line, const char *fmt, ...);
#else
#define TRACE(level, fmt, ...) (void) 0
#endif
endef
define ERR_H
#pragma once
extern void err (int eval, const char *fmt, ...);
endef
ifeq ($(USER),gv)
RC_COMMENT = "Built on $(TODAY) by <[email protected]>"
else
RC_COMMENT = ""
endif
define NGREP_RC
#include <winver.h>
#if defined(__clang__)
#define RC_COMPILER "clang-cl"
#elif defined(_MSC_VER)
#define RC_COMPILER "MSVC"
#else
#error "Unsupported compiler"
#endif
#define RC_VERSION $(VER_MAJOR),$(VER_MINOR),$(VER_MICRO),0
LANGUAGE 0x09,0x01
VS_VERSION_INFO VERSIONINFO
FILEVERSION RC_VERSION
PRODUCTVERSION RC_VERSION
FILEFLAGSMASK 0x3fL
FILEOS VOS__WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE 0x0L
FILEFLAGS 0
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "https://github.com/jpr5/ngrep"
VALUE "FileDescription", "ngrep.exe (" RC_COMPILER ", $(CPU))"
VALUE "FileVersion", "$(VERSION)"
VALUE "InternalName", "ngrep.exe"
VALUE "ProductName", "ngrep.exe; Copyright (c) 2025 Jordan Ritter"
VALUE "ProductVersion", "$(VERSION)"
VALUE "LegalCopyright", "GNU GENERAL PUBLIC LICENSE v2 or commercial licence"
VALUE "Comments", $(RC_COMMENT)
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
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
#
# MSVC or clang-cl 'line' directive
#
l = line.lstrip()
if l.startswith("#line") or l.startswith("# "):
line = line.replace (r"\\", "/")
print (line)
#
# Print a newline after a functions or structs
#
if l == "}" or l == "};":
print ("")
print ("Removed %d empty lines." % empty_lines, file=sys.stderr)
endef
define CHECK_FOR_UNUSED_LIBS_PY
import os, sys, colorama
map_file = sys.argv[1]
ignore_libs = [ "oldnames.lib" ]
colorama.init()
class State():
IDLE = 0
UNUSED = 1
class Color():
RESET = colorama.Style.RESET_ALL
RED = colorama.Fore.RED + colorama.Style.BRIGHT
WHITE = colorama.Fore.WHITE + colorama.Style.BRIGHT
def cprint (color, s):
print ("%s%s%s" % (color, s, Color.RESET))
def report (unused):
num = len(unused)
plural = [ "library", "libraries" ]
if num > 0:
cprint (Color.RED, "%d unused %s in %s:" % (num, plural[num > 1], map_file))
for u in unused:
print (" " + u)
cprint (Color.WHITE, "Done.\n")
def process (state):
unused_libs = []
f = open (map_file, "rt")
lines = f.readlines()
f.close()
for l in lines:
l = l.strip()
if l == "Unused libraries:":
state = State.UNUSED
continue
if state == State.UNUSED:
if l == "":
break
if os.path.basename(l).lower() not in ignore_libs:
unused_libs.append (l)
return unused_libs
report (process(State.IDLE))
endef
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment