Our package build process currently has a significant flaw: we first run `setup.py`, recording all compilation commands, then we rewrite these compilation commands to invoke `emcc` and replay them, and then we pray that the cross compiled executables ended up in the right place to go into the wheel. This is not a good strategy because the build script is allowed to implement arbitrary logic, and if it moves, renames, etc any of the output files then we lose track of them. This has repeatedly caused difficulty for us.

However, we also make no particularly significant use of the two pass approach. We can just do the simpler thing: capture the compiler commands as they occur, modify them as needed, and then run the fixed command.

I also added a patch to fix the numpy feature detection for wasm so that we don't have to include `_npyconfig.h` and `config.h`, numpy can generate them in the way it would for a native build. I opened a numpy PR that would fix the detection for us upstream:
https://github.com/numpy/numpy/pull/21154

This clears the way for us to switch to using pypa/build (as @henryiii has suggested) by removing our dependence on specific setuptools behavior.

This is on top of #2238.
No replay. Our package build process currently has a significant flaw: we first run `setup.py`, recording all compilation commands, then we rewrite these compilation commands to invoke `emcc` and replay them, and then we pray that the cross compiled executables ended up in the right place to go into the wheel. This is not a good strategy because the build script is allowed to implement arbitrary logic, and if it moves, renames, etc any of the output files then we lose track of them. This has repeatedly caused difficulty for us.

However, we also make no particularly significant use of the two pass approach. We can just do the simpler thing: capture the compiler commands as they occur, modify them as needed, and then run the fixed command.

I also added a patch to fix the numpy feature detection for wasm so that we don't have to include `_npyconfig.h` and `config.h`, numpy can generate them in the way it would for a native build. I opened a numpy PR that would fix the detection for us upstream:
https://github.com/numpy/numpy/pull/21154

This clears the way for us to switch to using pypa/build (as @henryiii has suggested) by removing our dependence on specific setuptools behavior.

This is on top of #2238.
From 3f11694d7587e782530118d176306eeb6eebf5d1 Mon Sep 17 00:00:00 2001
From: Hood <hood@mit.edu>
Date: Wed, 23 Jun 2021 13:47:30 -0700
Subject: [PATCH] Don't dereference function pointer

Ctypes thinks that the result of dlsym is a pointer to the function pointer, so
it should call it like `result = (*f)(args)`. Probably this is true for the
native dlsym, but our dlsym returns an index into the indirect call table
"wasmTable", in particular it isn't even aligned like a pointer should be.
This patch fixes it so that it calls it like `result = f(args)` instead.

---
 Modules/_ctypes/_ctypes.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c
index ceae67e..44f2d76 100644
--- a/Modules/_ctypes/_ctypes.c
+++ b/Modules/_ctypes/_ctypes.c
@@ -771,7 +771,11 @@ CDataType_in_dll(PyObject *type, PyObject *args)
         return NULL;
     }
 #endif
-    return PyCData_AtAddress(type, address);
+    CDataObject *ob = (CDataObject *)GenericPyCData_new(type, NULL, NULL);
+    if (ob == NULL)
+        return NULL;
+    *(void **)ob->b_ptr = address;
+    return (PyObject*)ob;
 }
 
 static const char from_param_doc[] =
-- 
2.17.1


From 3f11694d7587e782530118d176306eeb6eebf5d1 Mon Sep 17 00:00:00 2001
From: Hood <hood@mit.edu>
Date: Wed, 23 Jun 2021 13:47:30 -0700
Subject: [PATCH] Don't dereference function pointer

Ctypes thinks that the result of dlsym is a pointer to the function pointer, so
it should call it like `result = (*f)(args)`. Probably this is true for the
native dlsym, but our dlsym returns an index into the indirect call table
"wasmTable", in particular it isn't even aligned like a pointer should be.
This patch fixes it so that it calls it like `result = f(args)` instead.

---
 Modules/_ctypes/_ctypes.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c
index ceae67e..44f2d76 100644
--- a/Modules/_ctypes/_ctypes.c
+++ b/Modules/_ctypes/_ctypes.c
@@ -771,7 +771,11 @@ CDataType_in_dll(PyObject *type, PyObject *args)
         return NULL;
     }
 #endif
-    return PyCData_AtAddress(type, address);
+    CDataObject *ob = (CDataObject *)GenericPyCData_new(type, NULL, NULL);
+    if (ob == NULL)
+        return NULL;
+    *(void **)ob->b_ptr = address;
+    return (PyObject*)ob;
 }
 
 static const char from_param_doc[] =
-- 
2.17.1

