]> git.draconx.ca Git - dxcommon.git/commitdiff
Add a macro to probe -mwindows on MinGW.
authorNick Bowler <nbowler@draconx.ca>
Fri, 18 Nov 2022 02:55:34 +0000 (21:55 -0500)
committerNick Bowler <nbowler@draconx.ca>
Fri, 18 Nov 2022 02:55:34 +0000 (21:55 -0500)
MinGW is typically configured to build console applications by default.
So in order to build a normal Windows GUI application, this flag is
needed when linking.  Otherwise, a console window will be opened when
the program is run.

Other Windows compilers probably have similar options which can added to
the probe in the future.

m4/w32gui.m4 [new file with mode: 0644]
scripts/pe-subsys.awk [new file with mode: 0755]
t/ccw32.sh [new file with mode: 0755]
tests/macros.at

diff --git a/m4/w32gui.m4 b/m4/w32gui.m4
new file mode 100644 (file)
index 0000000..f333015
--- /dev/null
@@ -0,0 +1,32 @@
+dnl Copyright © 2022 Nick Bowler
+dnl
+dnl License WTFPL2: Do What The Fuck You Want To Public License, version 2.
+dnl This is free software: you are free to do what the fuck you want to.
+dnl There is NO WARRANTY, to the extent permitted by law.
+
+dnl DX_W32_GUI
+dnl
+dnl Determine the C linker options needed to build a Windows graphical
+dnl application (i.e., for the "windows" subsystem).  The result is saved
+dnl in the cache variable dx_cv_w32_gui_flags.
+dnl
+dnl If no suitable flags could be determined, the cache variable dx_cv_w32_gui
+dnl is set to "unknown" and dx_cv_w32_gui_flags is set to the empty string.
+
+AC_DEFUN([DX_W32_GUI], [AC_REQUIRE([AC_PROG_AWK])dnl
+AC_CACHE_CHECK([for $CC option to target W32 GUI], [dx_cv_w32_gui],
+[_dx_check_pe_subsys () {
+  $AWK -f m4_do([m4_pushdef([m4_include], [$][1])],
+                [m4_include(DX_BASEDIR[/scripts/pe-subsys.awk])],
+                [m4_popdef([m4_include])]) "conftest$EXEEXT"
+}
+dx_cv_w32_gui=unknown _dx_save_LDFLAGS=$LDFLAGS
+for dx_cv_w32_gui_flags in '' -mwindows; do
+LDFLAGS="$_dx_save_LDFLAGS $dx_cv_w32_gui_flags"
+AC_LINK_IFELSE([AC_LANG_PROGRAM],
+[AS_CASE([`_dx_check_pe_subsys`],
+[gui], [dx_cv_w32_gui=${dx_cv_w32_gui_flags:-none needed}; break]
+)])
+done
+test x"$dx_cv_w32_gui" != x"unknown" || dx_cv_w32_gui_flags=
+LDFLAGS=$_dx_save_LDFLAGS])])
diff --git a/scripts/pe-subsys.awk b/scripts/pe-subsys.awk
new file mode 100755 (executable)
index 0000000..62e9057
--- /dev/null
@@ -0,0 +1,145 @@
+#!/bin/awk -f
+#
+# Determine the subsystem of a PE32 executable.
+#
+# Copyright © 2022 Nick Bowler
+#
+# License WTFPL2: Do What The Fuck You Want To Public License, version 2.
+# This is free software: you are free to do what the fuck you want to.
+# There is NO WARRANTY, to the extent permitted by law.
+
+BEGIN {
+  filename = ARGV[1];
+  subsys = "unknown";
+  endian = "unknown";
+
+  if (!filename) {
+    exit(1);
+  }
+  probe_dumper(filename);
+
+  od_read("0");
+  if ($2 == "055115") {
+    endian = "little";
+  } else if ($2 == "046532") {
+    endian = "big";
+  } else {
+    exit(1);
+  }
+
+  od_read("74");
+  val = 65536*od_decode($3) + od_decode($2);
+
+  pe_offset = sprintf("%o", val);
+  pe32_offset = sprintf("%o", val+24);
+  subsys_offset = sprintf("%o", val+24+68);
+
+  od_read(pe_offset);
+  if ($2 != "042520" || $3 != "000000") {
+    # bad PE header
+    exit(1);
+  }
+
+  od_read(pe32_offset);
+  if ($2 != "000413" && $2 != "001013") {
+    # bad PE32(+) header
+    exit(1);
+  }
+
+  od_read(subsys_offset);
+  subsys = od_decode($2);
+  if (subsys == 2)
+    subsys = "gui";
+  else if (subsys == 3)
+    subsys = "console";
+  else
+    subsys = "unknown (" subsys ")";
+
+  exit(0);
+}
+
+END {
+  print subsys
+}
+
+function probe_dumper(filename)
+{
+  (cmd = "od " filename " +10 2>/dev/null") | getline
+  close(cmd)
+
+  sub(/^0*/, "", $1);
+  if ($1 == 10) {
+    dumper_style = "od_traditional";
+    return;
+  }
+
+  (cmd = "od -j010 " filename " 2>/dev/null") | getline
+  close(cmd)
+
+  sub(/^0*/, "", $1);
+  if ($1 == 10) {
+    dumper_style = "od_posix";
+    return;
+  }
+
+  exit(1);
+}
+
+function od_read(offset, cmd)
+{
+  if (dumper_style == "od_traditional") {
+    cmd = "od " filename " +" offset;
+  } else if (dumper_style == "od_posix") {
+    cmd = "od -j0" offset " " filename;
+  }
+
+  cmd | getline
+  close(cmd);
+
+  if (endian == "big") {
+    $2 = od_bswap($2);
+    $3 = od_bswap($3);
+  }
+}
+
+# Byte-swap one of the 2-byte blocks of octal digits emitted by od.
+function od_bswap(word, i, digits, carry)
+{
+  for (i = 0; i < 6; i++) {
+    digits[i] = substr(word, i+1, 1);
+  }
+
+  # suss out the first byte by adjusting the first 4 digits left by 1 bit
+  if (carry = (digits[3] >= 4))
+    digits[3] -= 4;
+
+  for (i = 2; i >= 0; i--) {
+    if (carry = ( (digits[i] = digits[i]*2 + carry) >= 8 ))
+      digits[i] -= 8;
+  }
+
+  # now munge the second byte by adjusting the last 3 digits right 1 bit
+  carry = 0;
+  for (i = 3; i < 6; i++) {
+    carry = (digits[i] += 8*carry) % 2;
+    digits[i] = int(digits[i] / 2);
+  }
+
+  # and put the leftover bit in place
+  digits[0] += 4*carry;
+
+  return digits[3] digits[4] digits[5] digits[0] digits[1] digits[2];
+}
+
+# Parse a string of octal digits into a number.
+function od_decode(word, result)
+{
+  result = 0;
+
+  while (word) {
+    result = 8*result + substr(word, 1, 1);
+    word = substr(word, 2);
+  }
+
+  return result;
+}
diff --git a/t/ccw32.sh b/t/ccw32.sh
new file mode 100755 (executable)
index 0000000..9a0fe7c
--- /dev/null
@@ -0,0 +1,47 @@
+#!/bin/sh
+#
+# Copyright © 2022 Nick Bowler
+#
+# Fake C compiler for testing PE subsystem probes.
+#
+# License WTFPL2: Do What The Fuck You Want To Public License, version 2.
+# This is free software: you are free to do what the fuck you want to.
+# There is NO WARRANTY, to the extent permitted by law.
+
+mode=link
+outfile=
+infile=
+
+: "${TEST_SUBSYS=3}"
+: "${TEST_GUI_FLAG=-mwindows}"
+
+for arg; do
+  shift
+
+  case $arg in
+  -o) outfile=$1; ;;
+  -c) mode=compile ;;
+  *.c) infile=$arg ;;
+  $TEST_GUI_FLAG) TEST_SUBSYS=2 ;;
+  esac
+done
+
+test x"$infile" != x || exit
+filebase=`expr "$infile" : '\(.*\).c$'`
+
+case $mode in
+link)
+  : "${outfile:=a.exe}"
+  ;;
+compile)
+  : "${outfile:=$filebase.obj}"
+  ;;
+esac
+
+:; {
+  printf '%-60s' MZ
+  printf '\101\1\0\0%257s' ''
+  printf 'PE\0\0%20s' ''
+  printf '\13\1%66s' ''
+  printf "\\$TEST_SUBSYS\\0"
+} >"$outfile"
index 99b93515b04ab5e882e5c76060dabd99dcc961ad..a085ad6ab50aeb2ff39ca7c300b5f17f60d0b9f9 100644 (file)
@@ -542,3 +542,58 @@ AT_CHECK([$SHELL exported.sh helpopt.lo], [0], [expout])
 AT_CHECK([$SHELL exported.sh nonexistent], [1], [], [ignore])
 
 AT_CLEANUP
+
+AT_SETUP([DX_W32_GUI])
+
+AT_DATA([test.in], [[@dx_cv_w32_gui@
+@dx_cv_w32_gui_flags@
+]])
+
+TEST_CONFIGURE_AC([[DX_W32_GUI
+AC_SUBST([dx_cv_w32_gui])
+AC_SUBST([dx_cv_w32_gui_flags])
+AC_CONFIG_FILES([test])
+]])
+TEST_AUTORECONF
+
+myconf="cross_compiling=yes --host=none CC=$srcdir/t/ccw32.sh"
+
+TEST_CONFIGURE([TEST_SUBSYS=2 TEST_GUI_FLAG=-mwindows $myconf])
+AT_CHECK([cat test], [0], [none needed
+
+])
+
+TEST_CONFIGURE([TEST_SUBSYS=3 TEST_GUI_FLAG=-mwindows $myconf])
+AT_CHECK([cat test], [0], [-mwindows
+-mwindows
+])
+
+TEST_CONFIGURE([TEST_SUBSYS=4 TEST_GUI_FLAG=xxx $myconf])
+AT_CHECK([cat test], [0], [unknown
+
+])
+
+AT_CLEANUP
+
+AT_SETUP([DX_W32_GUI distribution])
+AT_KEYWORDS([DX_W32_GUI macro])
+
+TEST_CONFIGURE_AC([[AM_INIT_AUTOMAKE([foreign])
+DX_W32_GUI
+AC_CONFIG_FILES([Makefile])
+]])
+
+AT_DATA([Makefile.am],
+[[foo: ; printf '%s\n' $(DX_BASEDIR) $(DISTFILES)
+]])
+TEST_AUTORECONF
+
+TEST_CONFIGURE
+AT_CHECK([make -s foo], [0], [stdout])
+AT_CHECK([exec 3<stdout
+read basedir <&3; while read f <&3; do
+  test x"$f" = x"$basedir/scripts/pe-subsys.awk" && exit
+done
+exit 1])
+
+AT_CLEANUP