Skip to content

Instantly share code, notes, and snippets.

@gvanem
Last active September 23, 2020 19:19
Show Gist options
  • Select an option

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

Select an option

Save gvanem/dc378254510f41c6760b176e18532422 to your computer and use it in GitHub Desktop.
MicroPython. GNU-makefile and a 'lib_main.c' for MSVC or clang-cl.
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2013, 2014 Damien P. George
* Copyright (c) 2014-2017 Paul Sokolovsky
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/*
* Cooked from 'ports/unix/main.c' and created as the entry-point,
* I.e. the 'mp_main()' for both the dynamic version of MicroPython (uPython.dll)
* and the static version (uPython.lib). Ref. 'Py_Init()' in CPython.
*
* And there is no need for a 'DllMain()'.
*
* Caveats:
* MICROPY_USE_READLINE == 1 is required
* Removed all code for:
* MICROPY_UNIX_COVERAGE
* MICROPY_PY_BLUETOOTH
*/
#include <windows.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <signal.h>
#if MICROPY_PY_LWIP
#include <lwip/init.h>
#include <lwip/sys.h>
#include <lwip/tcpip.h>
#include <lwip/dhcp.h>
#include <lwip/netif.h>
#include <lwip/apps/mdns.h>
#include <ports/win32/cfg_file.h>
#include <ports/win32/pcapif.h>
static struct netif netif;
static void mp_init_lwip (void);
#endif
#include "py/compile.h"
#include "py/runtime.h"
#include "py/builtin.h"
#include "py/repl.h"
#include "py/gc.h"
#include "py/stackctrl.h"
#include "py/mphal.h"
#include "py/mpthread.h"
#include "extmod/misc.h"
#include "extmod/vfs.h"
#include "extmod/vfs_posix.h"
#include "genhdr/mpversion.h"
#include "ports/unix/input.h"
#include "lib/mp-readline/readline.h"
#define PATHLIST_SEP_CHAR ';'
// Command line options, with their defaults
STATIC bool compile_only = false;
STATIC uint emit_opt = MP_EMIT_OPT_NONE;
#if MICROPY_ENABLE_GC
// Heap size of GC heap (if enabled)
// Make it larger on a 64 bit machine, because pointers are larger.
long heap_size = 1024 * 1024 * (sizeof(mp_uint_t) / 4);
#endif
STATIC void stderr_print_strn(void *env, const char *str, size_t len) {
(void)env;
ssize_t ret;
MP_HAL_RETRY_SYSCALL(ret, write(STDERR_FILENO, str, len), {});
mp_uos_dupterm_tx_strn(str, len);
}
const mp_print_t mp_stderr_print = {NULL, stderr_print_strn};
#define FORCED_EXIT (0x100)
// If exc is SystemExit, return value where FORCED_EXIT bit set,
// and lower 8 bits are SystemExit value. For all other exceptions,
// return 1.
STATIC int handle_uncaught_exception(mp_obj_base_t *exc) {
// check for SystemExit
if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(exc->type), MP_OBJ_FROM_PTR(&mp_type_SystemExit))) {
// None is an exit value of 0; an int is its value; anything else is 1
mp_obj_t exit_val = mp_obj_exception_get_value(MP_OBJ_FROM_PTR(exc));
mp_int_t val = 0;
if (exit_val != mp_const_none && !mp_obj_get_int_maybe(exit_val, &val)) {
val = 1;
}
return FORCED_EXIT | (val & 255);
}
// Report all other exceptions
mp_obj_print_exception(&mp_stderr_print, MP_OBJ_FROM_PTR(exc));
return 1;
}
#define LEX_SRC_STR (1)
#define LEX_SRC_VSTR (2)
#define LEX_SRC_FILENAME (3)
#define LEX_SRC_STDIN (4)
// Returns standard error codes: 0 for success, 1 for all other errors,
// except if FORCED_EXIT bit is set then script raised SystemExit and the
// value of the exit is in the lower 8 bits of the return value
STATIC int execute_from_lexer(int source_kind, const void *source, mp_parse_input_kind_t input_kind, bool is_repl) {
mp_hal_set_interrupt_char(CHAR_CTRL_C);
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
// create lexer based on source kind
mp_lexer_t *lex;
if (source_kind == LEX_SRC_STR) {
const char *line = source;
lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, line, strlen(line), false);
} else if (source_kind == LEX_SRC_VSTR) {
const vstr_t *vstr = source;
lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr->buf, vstr->len, false);
} else if (source_kind == LEX_SRC_FILENAME) {
lex = mp_lexer_new_from_file((const char *)source);
} else { // LEX_SRC_STDIN
lex = mp_lexer_new_from_fd(MP_QSTR__lt_stdin_gt_, 0, false);
}
qstr source_name = lex->source_name;
#if MICROPY_PY___FILE__
if (input_kind == MP_PARSE_FILE_INPUT) {
mp_store_global(MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name));
}
#endif
mp_parse_tree_t parse_tree = mp_parse(lex, input_kind);
mp_obj_t module_fun = mp_compile(&parse_tree, source_name, is_repl);
if (!compile_only) {
// execute it
mp_call_function_0(module_fun);
}
mp_hal_set_interrupt_char(-1);
mp_handle_pending(true);
nlr_pop();
return 0;
} else {
// uncaught exception
mp_hal_set_interrupt_char(-1);
mp_handle_pending(false);
return handle_uncaught_exception(nlr.ret_val);
}
}
STATIC int do_repl(void) {
mp_hal_stdout_tx_str("MicroPython " MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE "; "
MICROPY_PY_SYS_PLATFORM " version\nUse Ctrl-D to exit, Ctrl-E for paste mode\n");
// use MicroPython supplied readline
vstr_t line;
vstr_init(&line, 16);
for (;;) {
mp_hal_stdio_mode_raw();
input_restart:
vstr_reset(&line);
int ret = readline(&line, ">>> ");
mp_parse_input_kind_t parse_input_kind = MP_PARSE_SINGLE_INPUT;
if (ret == CHAR_CTRL_C) {
// cancel input
mp_hal_stdout_tx_str("\r\n");
goto input_restart;
} else if (ret == CHAR_CTRL_D) {
// EOF
printf("\n");
mp_hal_stdio_mode_orig();
vstr_clear(&line);
return 0;
} else if (ret == CHAR_CTRL_E) {
// paste mode
mp_hal_stdout_tx_str("\npaste mode; Ctrl-C to cancel, Ctrl-D to finish\n=== ");
vstr_reset(&line);
for (;;) {
char c = mp_hal_stdin_rx_chr();
if (c == CHAR_CTRL_C) {
// cancel everything
mp_hal_stdout_tx_str("\n");
goto input_restart;
} else if (c == CHAR_CTRL_D) {
// end of input
mp_hal_stdout_tx_str("\n");
break;
} else {
// add char to buffer and echo
vstr_add_byte(&line, c);
if (c == '\r') {
mp_hal_stdout_tx_str("\n=== ");
} else {
mp_hal_stdout_tx_strn(&c, 1);
}
}
}
parse_input_kind = MP_PARSE_FILE_INPUT;
} else if (line.len == 0) {
if (ret != 0) {
printf("\n");
}
goto input_restart;
} else {
// got a line with non-zero length, see if it needs continuing
while (mp_repl_continue_with_input(vstr_null_terminated_str(&line))) {
vstr_add_byte(&line, '\n');
ret = readline(&line, "... ");
if (ret == CHAR_CTRL_C) {
// cancel everything
printf("\n");
goto input_restart;
} else if (ret == CHAR_CTRL_D) {
// stop entering compound statement
break;
}
}
}
mp_hal_stdio_mode_orig();
ret = execute_from_lexer(LEX_SRC_VSTR, &line, parse_input_kind, true);
if (ret & FORCED_EXIT) {
return ret;
}
}
}
STATIC int do_file(const char *file) {
return execute_from_lexer(LEX_SRC_FILENAME, file, MP_PARSE_FILE_INPUT, false);
}
STATIC int do_str(const char *str) {
return execute_from_lexer(LEX_SRC_STR, str, MP_PARSE_FILE_INPUT, false);
}
STATIC void print_help(char **argv) {
printf(
"usage: %s [<opts>] [-X <implopt>] [-c <command> | -m <module> | <filename>]\n"
"Options:\n"
"-h : print this help message\n"
"-i : enable inspection via REPL after running command/module/file\n"
#if MICROPY_DEBUG_PRINTERS
"-v : verbose (trace various operations); can be multiple\n"
#endif
"-O[N] : apply bytecode optimizations of level N\n"
"\n"
"Implementation specific options (-X):\n", argv[0]
);
int impl_opts_cnt = 0;
printf(
" compile-only -- parse and compile only\n"
#if MICROPY_EMIT_NATIVE
" emit={bytecode,native,viper} -- set the default code emitter\n"
#else
" emit=bytecode -- set the default code emitter\n"
#endif
);
impl_opts_cnt++;
#if MICROPY_ENABLE_GC
printf(
" heapsize=<n>[w][K|M] -- set the heap size for the GC (default %ld)\n"
, heap_size);
impl_opts_cnt++;
#endif
if (impl_opts_cnt == 0) {
printf(" (none)\n");
}
}
STATIC int invalid_args(void) {
fprintf(stderr, "Invalid command line arguments. Use -h option for help.\n");
return 1;
}
// Process options which set interpreter init options
STATIC void pre_process_options(int argc, char **argv) {
for (int a = 1; a < argc; a++) {
if (argv[a][0] == '-') {
if (strcmp(argv[a], "-h") == 0) {
print_help(argv);
exit(0);
}
if (strcmp(argv[a], "-X") == 0) {
if (a + 1 >= argc) {
exit(invalid_args());
}
if (0) {
} else if (strcmp(argv[a + 1], "compile-only") == 0) {
compile_only = true;
} else if (strcmp(argv[a + 1], "emit=bytecode") == 0) {
emit_opt = MP_EMIT_OPT_BYTECODE;
#if MICROPY_EMIT_NATIVE
} else if (strcmp(argv[a + 1], "emit=native") == 0) {
emit_opt = MP_EMIT_OPT_NATIVE_PYTHON;
} else if (strcmp(argv[a + 1], "emit=viper") == 0) {
emit_opt = MP_EMIT_OPT_VIPER;
#endif
#if MICROPY_ENABLE_GC
} else if (strncmp(argv[a + 1], "heapsize=", sizeof("heapsize=") - 1) == 0) {
char *end;
heap_size = strtol(argv[a + 1] + sizeof("heapsize=") - 1, &end, 0);
// Don't bring unneeded libc dependencies like tolower()
// If there's 'w' immediately after number, adjust it for
// target word size. Note that it should be *before* size
// suffix like K or M, to avoid confusion with kilowords,
// etc. the size is still in bytes, just can be adjusted
// for word size (taking 32bit as baseline).
bool word_adjust = false;
if ((*end | 0x20) == 'w') {
word_adjust = true;
end++;
}
if ((*end | 0x20) == 'k') {
heap_size *= 1024;
} else if ((*end | 0x20) == 'm') {
heap_size *= 1024 * 1024;
} else {
// Compensate for ++ below
--end;
}
if (*++end != 0) {
goto invalid_arg;
}
if (word_adjust) {
heap_size = heap_size * BYTES_PER_WORD / 4;
}
// If requested size too small, we'll crash anyway
if (heap_size < 700) {
goto invalid_arg;
}
#endif
} else {
invalid_arg:
exit(invalid_args());
}
a++;
}
}
}
}
STATIC void set_sys_argv(char *argv[], int argc, int start_arg) {
for (int i = start_arg; i < argc; i++) {
mp_obj_list_append(mp_sys_argv, MP_OBJ_NEW_QSTR(qstr_from_str(argv[i])));
}
}
int mp_main (int argc, char **argv)
{
#if MICROPY_PY_THREAD
mp_thread_init();
#endif
// We should capture stack top ASAP after start, and it should be
// captured guaranteedly before any other stack variables are allocated.
// For this, actual main (renamed mp_main) should not be inlined into
// this function. mp_main() itself may have other functions inlined (with
// their own stack variables), that's why we need this main/mp_main split.
mp_stack_ctrl_init();
#if MICROPY_PY_LWIP
mp_init_lwip();
#endif
#ifdef SIGPIPE
// Do not raise SIGPIPE, instead return EPIPE. Otherwise, e.g. writing
// to peer-closed socket will lead to sudden termination of MicroPython
// process. SIGPIPE is particularly nasty, because unix shell doesn't
// print anything for it, so the above looks like completely sudden and
// silent termination for unknown reason. Ignoring SIGPIPE is also what
// CPython does. Note that this may lead to problems using MicroPython
// scripts as pipe filters, but again, that's what CPython does. So,
// scripts which want to follow unix shell pipe semantics (where SIGPIPE
// means "pipe was requested to terminate, it's not an error"), should
// catch EPIPE themselves.
signal(SIGPIPE, SIG_IGN);
#endif
mp_stack_set_limit(40000 * (BYTES_PER_WORD / 4));
pre_process_options(argc, argv);
#if MICROPY_ENABLE_GC
char *heap = malloc(heap_size);
gc_init(heap, heap + heap_size);
#endif
#if MICROPY_ENABLE_PYSTACK
static mp_obj_t pystack[1024];
mp_pystack_init(pystack, &pystack[MP_ARRAY_SIZE(pystack)]);
#endif
mp_init();
#if MICROPY_EMIT_NATIVE
// Set default emitter options
MP_STATE_VM(default_emit_opt) = emit_opt;
#else
(void)emit_opt;
#endif
#if MICROPY_VFS_POSIX
{
// Mount the host FS at the root of our internal VFS
mp_obj_t args[2] = {
mp_type_vfs_posix.make_new(&mp_type_vfs_posix, 0, 0, NULL),
MP_OBJ_NEW_QSTR(MP_QSTR__slash_),
};
mp_vfs_mount(2, args, (mp_map_t *)&mp_const_empty_map);
MP_STATE_VM(vfs_cur) = MP_STATE_VM(vfs_mount_table);
}
#endif
char *home = getenv("APPDATA");
char *path = getenv("MICROPYPATH");
if (path == NULL) {
#ifdef MICROPY_PY_SYS_PATH_DEFAULT
path = MICROPY_PY_SYS_PATH_DEFAULT;
#else
path = "~/.micropython/lib:/usr/lib/micropython";
#endif
}
size_t path_num = 1; // [0] is for current dir (or base dir of the script)
if (*path == PATHLIST_SEP_CHAR) {
path_num++;
}
for (char *p = path; p != NULL; p = strchr(p, PATHLIST_SEP_CHAR)) {
path_num++;
if (p != NULL) {
p++;
}
}
mp_obj_list_init(MP_OBJ_TO_PTR(mp_sys_path), path_num);
mp_obj_t *path_items;
mp_obj_list_get(mp_sys_path, &path_num, &path_items);
path_items[0] = MP_OBJ_NEW_QSTR(MP_QSTR_);
{
char *p = path;
for (mp_uint_t i = 1; i < path_num; i++) {
char *p1 = strchr(p, PATHLIST_SEP_CHAR);
if (p1 == NULL) {
p1 = p + strlen(p);
}
if (p[0] == '~' && p[1] == '/' && home != NULL) {
// Expand standalone ~ to $HOME
int home_l = strlen(home);
vstr_t vstr;
vstr_init(&vstr, home_l + (p1 - p - 1) + 1);
vstr_add_strn(&vstr, home, home_l);
vstr_add_strn(&vstr, p + 1, p1 - p - 1);
path_items[i] = mp_obj_new_str_from_vstr(&mp_type_str, &vstr);
} else {
path_items[i] = mp_obj_new_str_via_qstr(p, p1 - p);
}
p = p1 + 1;
}
}
mp_obj_list_init(MP_OBJ_TO_PTR(mp_sys_argv), 0);
// Here is some example code to create a class and instance of that class.
// First is the Python, then the C code.
//
// class TestClass:
// pass
// test_obj = TestClass()
// test_obj.attr = 42
//
// mp_obj_t test_class_type, test_class_instance;
// test_class_type = mp_obj_new_type(QSTR_FROM_STR_STATIC("TestClass"), mp_const_empty_tuple, mp_obj_new_dict(0));
// mp_store_name(QSTR_FROM_STR_STATIC("test_obj"), test_class_instance = mp_call_function_0(test_class_type));
// mp_store_attr(test_class_instance, QSTR_FROM_STR_STATIC("attr"), mp_obj_new_int(42));
/*
printf("bytes:\n");
printf(" total %d\n", m_get_total_bytes_allocated());
printf(" cur %d\n", m_get_current_bytes_allocated());
printf(" peak %d\n", m_get_peak_bytes_allocated());
*/
const int NOTHING_EXECUTED = -2;
int ret = NOTHING_EXECUTED;
bool inspect = false;
for (int a = 1; a < argc; a++) {
if (argv[a][0] == '-') {
if (strcmp(argv[a], "-i") == 0) {
inspect = true;
} else if (strcmp(argv[a], "-c") == 0) {
if (a + 1 >= argc) {
return invalid_args();
}
ret = do_str(argv[a + 1]);
if (ret & FORCED_EXIT) {
break;
}
a += 1;
} else if (strcmp(argv[a], "-m") == 0) {
if (a + 1 >= argc) {
return invalid_args();
}
mp_obj_t import_args[4];
import_args[0] = mp_obj_new_str(argv[a + 1], strlen(argv[a + 1]));
import_args[1] = import_args[2] = mp_const_none;
// Ask __import__ to handle imported module specially - set its __name__
// to __main__, and also return this leaf module, not top-level package
// containing it.
import_args[3] = mp_const_false;
// TODO: https://docs.python.org/3/using/cmdline.html#cmdoption-m :
// "the first element of sys.argv will be the full path to
// the module file (while the module file is being located,
// the first element will be set to "-m")."
set_sys_argv(argv, argc, a + 1);
mp_obj_t mod;
nlr_buf_t nlr;
bool subpkg_tried = false;
reimport:
if (nlr_push(&nlr) == 0) {
mod = mp_builtin___import__(MP_ARRAY_SIZE(import_args), import_args);
nlr_pop();
} else {
// uncaught exception
return handle_uncaught_exception(nlr.ret_val) & 0xff;
}
if (mp_obj_is_package(mod) && !subpkg_tried) {
subpkg_tried = true;
vstr_t vstr;
int len = strlen(argv[a + 1]);
vstr_init(&vstr, len + sizeof(".__main__"));
vstr_add_strn(&vstr, argv[a + 1], len);
vstr_add_strn(&vstr, ".__main__", sizeof(".__main__") - 1);
import_args[0] = mp_obj_new_str_from_vstr(&mp_type_str, &vstr);
goto reimport;
}
ret = 0;
break;
} else if (strcmp(argv[a], "-X") == 0) {
a += 1;
#if MICROPY_DEBUG_PRINTERS
} else if (strcmp(argv[a], "-v") == 0) {
mp_verbose_flag++;
#endif
} else if (strncmp(argv[a], "-O", 2) == 0) {
if (unichar_isdigit(argv[a][2])) {
MP_STATE_VM(mp_optimise_value) = argv[a][2] & 0xf;
} else {
MP_STATE_VM(mp_optimise_value) = 0;
for (char *p = argv[a] + 1; *p && *p == 'O'; p++, MP_STATE_VM(mp_optimise_value)++) {;
}
}
} else {
return invalid_args();
}
} else {
char *pathbuf = malloc(PATH_MAX);
char *basedir = realpath(argv[a], pathbuf);
if (basedir == NULL) {
mp_printf(&mp_stderr_print, "%s: can't open file '%s': [Errno %d] %s\n", argv[0], argv[a], errno, strerror(errno));
// CPython exits with 2 in such case
ret = 2;
break;
}
// Set base dir of the script as first entry in sys.path
char *p = strrchr(basedir, '/');
path_items[0] = mp_obj_new_str_via_qstr(basedir, p - basedir);
free(pathbuf);
set_sys_argv(argv, argc, a);
ret = do_file(argv[a]);
break;
}
}
const char *inspect_env = getenv("MICROPYINSPECT");
if (inspect_env && inspect_env[0] != '\0') {
inspect = true;
}
if (ret == NOTHING_EXECUTED || inspect) {
if (isatty(0) || inspect) {
prompt_read_history();
ret = do_repl();
prompt_write_history();
} else {
ret = execute_from_lexer(LEX_SRC_STDIN, NULL, MP_PARSE_FILE_INPUT, false);
}
}
#if MICROPY_PY_SYS_SETTRACE
MP_STATE_THREAD(prof_trace_callback) = MP_OBJ_NULL;
#endif
#if MICROPY_PY_SYS_ATEXIT
// Beware, the sys.settrace callback should be disabled before running sys.atexit.
if (mp_obj_is_callable(MP_STATE_VM(sys_exitfunc))) {
mp_call_function_0(MP_STATE_VM(sys_exitfunc));
}
#endif
#if MICROPY_PY_MICROPYTHON_MEM_INFO
if (mp_verbose_flag) {
mp_micropython_mem_info(0, NULL);
}
#endif
#if MICROPY_PY_THREAD
mp_thread_deinit();
#endif
mp_deinit();
#if MICROPY_ENABLE_GC && !defined(NDEBUG)
// We don't really need to free memory since we are about to exit the
// process, but doing so helps to find memory leaks.
free(heap);
#endif
// printf("total bytes = %d\n", m_get_total_bytes_allocated());
return ret & 0xff;
}
int mp_import_stat(const char *path) {
struct stat st;
if (stat(path, &st) == 0) {
if (S_ISDIR(st.st_mode)) {
return MP_IMPORT_STAT_DIR;
} else if (S_ISREG(st.st_mode)) {
return MP_IMPORT_STAT_FILE;
}
}
return MP_IMPORT_STAT_NO_EXIST;
}
#if MICROPY_PY_IO
// Factory function for I/O stream classes, only needed if generic VFS subsystem isn't used.
// Note: buffering and encoding are currently ignored.
mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kwargs) {
enum { ARG_file, ARG_mode };
STATIC const mp_arg_t allowed_args[] = {
{ MP_QSTR_file, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_mode, MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_QSTR(MP_QSTR_r)} },
{ MP_QSTR_buffering, MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_encoding, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kwargs, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
return mp_vfs_posix_file_open(&mp_type_textio, args[ARG_file].u_obj, args[ARG_mode].u_obj);
}
MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open);
#endif
void nlr_jump_fail(void *val) {
fprintf(stderr, "FATAL: uncaught NLR %p\n", val);
exit(1);
}
#if MICROPY_PY_LWIP /* Rest of file */
static void status_callback (struct netif *state_netif)
{
if (netif_is_up(state_netif))
{
LWIP_DEBUGF (LWIP_DBG_TRACE, ("status_callback == UP, local interface IP is %s\n",
ip4addr_ntoa(netif_ip4_addr(state_netif))));
#if LWIP_MDNS_RESPONDER
// mdns_resp_netif_settings_changed (state_netif);
#endif
}
else
LWIP_DEBUGF (LWIP_DBG_TRACE, ("status_callback == DOWN\n"));
}
static void link_callback (struct netif *state_netif)
{
LWIP_DEBUGF (LWIP_DBG_TRACE, ("link_callback == %s\n", netif_is_link_up(state_netif) ? "UP" : "DOWN"));
}
/* This function initializes all network interfaces
*/
static void mp_netif_init (void)
{
ip4_addr_t ipaddr, netmask, gw;
err_t err;
#define NETIF_ADDRS &ipaddr, &netmask, &gw,
ip4_addr_set_zero (&gw);
ip4_addr_set_zero (&ipaddr);
ip4_addr_set_zero (&netmask);
#if LWIP_DHCP
LWIP_DEBUGF (LWIP_DBG_TRACE, ("Starting lwIP, local interface IP is dhcp-enabled\n"));
#elif LWIP_AUTOIP
LWIP_DEBUGF (LWIP_DBG_TRACE, ("Starting lwIP, local interface IP is autoip-enabled\n"));
#else
LWIP_PORT_INIT_GW (&gw);
LWIP_PORT_INIT_IPADDR (&ipaddr);
LWIP_PORT_INIT_NETMASK (&netmask);
LWIP_DEBUGF (LWIP_DBG_TRACE, ("Starting lwIP, local interface IP is %s\n", ip4addr_ntoa(&ipaddr)));
#endif
netif_set_default (netif_add(&netif, NETIF_ADDRS NULL, pcapif_init, tcpip_input));
#if LWIP_NETIF_STATUS_CALLBACK
netif_set_status_callback (&netif, status_callback);
#endif
#if LWIP_NETIF_LINK_CALLBACK
netif_set_link_callback (&netif, link_callback);
#endif
netif_set_up (&netif);
if (lwip_cfg.ip4.fixed_addr)
{
netif_set_ipaddr (&netif, (const ip4_addr_t*) &lwip_cfg.ip4.fixed_addr);
netif_set_netmask (&netif, (const ip4_addr_t*) &lwip_cfg.ip4.fixed_netmask);
netif_set_gw (&netif, (const ip4_addr_t*) &lwip_cfg.ip4.fixed_gateway);
}
else
{
err = dhcp_start (&netif);
LWIP_ASSERT ("dhcp_start failed", err == ERR_OK);
}
}
/* This function initializes all network apps.
* Which should we do?
*/
static void mp_apps_init (void)
{
}
static void test_init (void *arg)
{
sys_sem_t *init_sem;
LWIP_DEBUGF (LWIP_DBG_TRACE, ("In %s().\n", __FUNCTION__));
LWIP_ASSERT ("arg != NULL", arg != NULL);
init_sem = (sys_sem_t*)arg;
srand ((unsigned int)time(NULL));
mp_netif_init();
mp_apps_init();
sys_sem_signal (init_sem);
}
static void mp_init_lwip (void)
{
sys_sem_t init_sem;
err_t err;
_lwip_trace_start (0, NULL, 0);
err = sys_sem_new (&init_sem, 0);
LWIP_ASSERT ("failed to create init_sem", err == ERR_OK);
tcpip_init (test_init, &init_sem);
/* we have to wait for initialization to finish before
* calling update_adapter()!
*/
sys_sem_wait (&init_sem);
sys_sem_free (&init_sem);
#if LWIP_NETCONN_SEM_PER_THREAD
netconn_thread_init();
#endif
#if LWIP_MDNS_RESPONDER
// mdns_resp_init();
#endif
}
#endif /* MICROPY_PY_LWIP */
#
# Gnu Makefile for MicroPython (MSVC or clang-cl).
#
# This Makefile MUST be placed in the root-directory of your MicroPython installation.
#
# By G. Vanem <[email protected]> 2020.
#
THIS_FILE = Makefile.Windows
TODAY = $(shell date +%d-%B-%Y)
#
# Set 'MDEPEND=' to NOT rebuild everything when this file changes.
# It might be a good idea to delete '.depend.Windows' too.
#
MDEPEND = $(THIS_FILE)
#
# Support for the 'MbedTLS' crypto library.
#
USE_MBEDTLS ?= 1
#
# Support for the 'LwIP' tcp/ip-stack.
#
USE_LWIP ?= 1
#
# Support for 'Virtual File System'.
#
USE_VFS ?= 1
#
# Support for a builtin "help()" command in the console.
#
USE_HELP_MODULES ?= 1
#
# Let 'microPython.exe' use 'uPython.dll'
#
USE_uPython_DLL ?= 1
#
# Use only dynamic foreign libraries for 'uPython.dll' (and 'MicroPython.exe' if 'USE_uPython_DLL=0').
#
# With this option (and 'USE_LWIP=1' and 'USE_MBEDTLS=1', the dependencies looks like this:
#
# MicroPython.exe 11 kB!
# uPython.dll 599 kB
# lwip32.dll 259 kB
# wpcap.dll
# packet.dll
# mbedcrypto.dll 622 kB
# mbedtls.dll 190 kB
# mbedcrypto.dll
# mbedx509.dll 68 kB
# mbedcrypto.dll
#
# As opposed to a 'uPython.dll' linked to only static libraries (except wpcap.dll):
#
# MicroPython.exe still only 11 kB
# uPython.dll 1327 kB
# wpcap.dll
# packet.dll
#
USE_FOREIGN_DLLs ?= 1
#
# For the '%.i' pre-processing rules below.
#
USE_CLANG_FORMATER ?= 0
USE_GNU_INDENT ?= 0
#
# Compile a lot faster; MSVC only.
#
USE_MP_COMPILE ?= 1
#
# For generating a lot of stuff.
#
PYTHON ?= py -3
VPATH = ports/windows \
ports/windows/msvc \
ports/unix \
extmod \
py \
lib/embed \
lib/mp-readline \
lib/netutils \
lib/oofatfs \
lib/timeutils \
lib/utils
#
# Extract from 'py/mpconfig.h':
#
get_ver_digit = $(shell grep --max-count=1 '#define MICROPY_VERSION_$(strip $(1))' py/mpconfig.h | cut -d' ' -f3)
VER_MAJOR = $(call get_ver_digit, MAJOR)
VER_MINOR = $(call get_ver_digit, MINOR)
VER_MICRO = $(call get_ver_digit, MICRO)
VERSION = $(VER_MAJOR).$(VER_MINOR).$(VER_MICRO)
#
# Root directories; change to suite:
#
LWIP_ROOT ?= f:/MinGW32/src/inet/LwIP
MBEDTLS_ROOT ?= f:/MinGW32/src/inet/Crypto/MbedTLS
PCAP_ROOT ?= f:/MinGW32/src/inet/libpcap
WINPCAP_ROOT ?= f:/MinGW32/src/inet/libpcap/WinPcap
#
# What to build:
#
# The static library cannot be called 'MicroPython.lib' since MSVC link
# wants to export stuff in the 'link_EXE, MicroPython.exe' command.
# But only when 'USE_uPython_DLL=0'.
#
STAT_LIB = uPython.lib
IMP_LIB = uPython_imp.lib
TARGETS = $(STAT_LIB) \
$(IMP_LIB) \
uPython.dll \
MicroPython.exe
define Usage
Usage: make -f $(THIS_FILE) CC=[cl | clang-cl] [all | depend | clean | vclean | examples]
Specify CC=cl - use MSVC
Specify CC=clang-cl - use clang-cl
endef
#
# A list of .obj-files from .c-files:
#
c_to_obj = $(addprefix $(OBJ_DIR)/, $(notdir $(1:.c=.obj)))
#
# This confuses 'clang-cl'. Drop it.
#
export CL=
ifeq ($(CC),cl)
RCFLAGS = -nologo -D_MSC_VER
else ifeq ($(CC),clang-cl)
RCFLAGS = -nologo -D__clang__
USE_MP_COMPILE = 0
else
$(error $(Usage))
endif
OBJ_DIR = objects
#
# Made by '$(call make_pp_file ...)' rules below:
#
Preprocessed_files = $(addprefix $(OBJ_DIR)/genhdr/qstr/, \
$(LIB_SRC:.c=.pp) \
ports/unix/main.pp)
#
# The generate-order for these is important:
#
GENERATED = $(addprefix $(OBJ_DIR)/genhdr/, \
moduledefs.h \
mpversion.h) \
\
$(Preprocessed_files) \
\
$(addprefix $(OBJ_DIR)/genhdr/, \
qstrdefs.collected.h \
qstrdefs.generated.h \
qstrdefs.preprocessed.h)
LDFLAGS = -nologo -debug -incremental:no -verbose -subsystem:console -safeseh
CFLAGS = -nologo -MD -W3 -Zi -Zo \
-D_CRT_SECURE_NO_WARNINGS \
-D_CRT_SECURE_NO_WARNINGS \
-D_CRT_NONSTDC_NO_WARNINGS \
-D_WIN32_WINNT=0x0601 \
-Oi -Ot -Oy
CFLAGS += -D_USE_MATH_DEFINES \
-I. -I./ports/windows \
-I./ports/windows/msvc \
-I./$(OBJ_DIR)
ifeq ($(CC),clang-cl)
CFLAGS += -ferror-limit=5 \
-fms-compatibility
endif
#
# NLR = 'Non Local Return'. We MUST use 'setjmp()' + 'longjmp()'.
# Add some debug to it too.
#
CFLAGS += -DMICROPY_NLR_SETJMP=1 \
-DMICROPY_DEBUG_NLR=1
# CFLAGS += -DMICROPY_CPYTHON_COMPAT=0
CFLAGS += -DFFCONF_H=\"lib/oofatfs/ffconf.h\"
ifeq ($(CC),gcc)
CFLAGS += -D__USE_MINGW_ANSI_STDIO=1
endif
ifeq ($(USE_VFS),1)
CFLAGS += -DMICROPY_VFS=1 \
-DMICROPY_VFS_FAT=1 \
-DMICROPY_FATFS_RPATH=2
endif
#
# Check if these are possible on Windows:
#
CFLAGS += -DMICROPY_PY_SYS_SETTRACE=1 \
-DMICROPY_PERSISTENT_CODE_SAVE=1 \
-DMICROPY_COMP_CONST=0 \
-DMICROPY_PY_SYS_ATEXIT=1 \
-DMICROPY_EMIT_X86=1
#
# We should never need this.
#
LDFLAGS += -nodefaultlib:uuid.lib
#
# Warning control:
#
ifeq ($(CC),cl)
CFLAGS += -wd4005 -wd4113 -wd4146 -wd4244 -wd4267
else
CFLAGS += -Wno-microsoft-include \
-Wno-visibility \
-Wno-pragma-pack \
-Wno-pointer-sign \
-Wno-macro-redefined \
-Wno-incompatible-pointer-types
endif
#
# $(LIB_SRC): .c-sources for 'MicroPython.lib'
#
LIB_SRC = $(addprefix ports/windows/, \
fmode.c \
init.c \
lib_main.c \
realpath.c \
sleep.c \
windows_mphal.c)
ifneq ($(CC),gcc)
LIB_SRC += $(addprefix ports/windows/msvc/, \
dirent.c \
gettimeofday.c)
endif
LIB_SRC += $(addprefix ports/unix/, \
gccollect.c \
input.c \
modos.c \
modtime.c \
modmachine.c \
moduselect.c \
modusocket.c)
LIB_SRC += $(addprefix py/, \
argcheck.c \
asmbase.c \
asmx64.c \
asmx86.c \
bc.c \
binary.c \
builtinevex.c \
builtinhelp.c \
builtinimport.c \
compile.c \
emitbc.c \
emitcommon.c \
emitglue.c \
emitnative.c \
emitnx64.c \
emitnx86.c \
formatfloat.c \
frozenmod.c \
gc.c \
lexer.c \
malloc.c \
map.c \
modarray.c \
modbuiltins.c \
modcmath.c \
modcollections.c \
modgc.c \
modio.c \
modmath.c \
modmicropython.c \
modstruct.c \
modsys.c \
modthread.c \
moduerrno.c \
mpprint.c \
mpstate.c \
mpz.c \
nativeglue.c \
nlr.c \
nlrsetjmp.c \
nlrx64.c \
nlrx86.c \
obj.c \
objarray.c \
objattrtuple.c \
objbool.c \
objboundmeth.c \
objcell.c \
objclosure.c \
objcomplex.c \
objdeque.c \
objdict.c \
objenumerate.c \
objexcept.c \
objfilter.c \
objfloat.c \
objfun.c \
objgenerator.c \
objgetitemiter.c \
objint.c \
objint_longlong.c \
objint_mpz.c \
objlist.c \
objmap.c \
objmodule.c \
objnamedtuple.c \
objnone.c \
objobject.c \
objpolyiter.c \
objproperty.c \
objrange.c \
objreversed.c \
objset.c \
objsingleton.c \
objslice.c \
objstr.c \
objstringio.c \
objstrunicode.c \
objtuple.c \
objtype.c \
objzip.c \
opmethods.c \
pairheap.c \
parse.c \
parsenum.c \
parsenumbase.c \
persistentcode.c \
profile.c \
pystack.c \
qstr.c \
reader.c \
repl.c \
ringbuf.c \
runtime.c \
runtime_utils.c \
scheduler.c \
scope.c \
sequence.c \
showbc.c \
smallint.c \
stackctrl.c \
stream.c \
unicode.c \
vm.c \
vstr.c \
warning.c)
LIB_SRC += $(addprefix extmod/, \
machine_mem.c \
machine_pinbase.c \
machine_pulse.c \
machine_signal.c \
modubinascii.c \
moducryptolib.c \
moductypes.c \
modframebuf.c \
moduhashlib.c \
moduheapq.c \
modujson.c \
modurandom.c \
modure.c \
modutimeq.c \
moduwebsocket.c \
modwebrepl.c \
moduzlib.c \
uos_dupterm.c \
utime_mphal.c \
vfs.c \
vfs_blockdev.c \
vfs_fat.c \
vfs_fat_diskio.c \
vfs_fat_file.c \
vfs_lfs.c \
vfs_posix.c \
vfs_posix_file.c \
vfs_reader.c \
virtpin.c)
LIB_SRC += lib/embed/abort_.c \
lib/mp-readline/readline.c \
lib/timeutils/timeutils.c \
lib/utils/gchelper_generic.c \
lib/utils/printf.c
#
# VFS on Windows!!? Will it do anything sensible?
#
ifeq ($(USE_VFS),1)
LIB_SRC += extmod/vfs_fat_diskio.c \
lib/oofatfs/ff.c \
lib/oofatfs/ffunicode.c \
ports/unix/fatfs_port.c
endif
#
# We never need these on Windows:
#
# LIB_SRC += $(addprefix py/, \
# asmarm.c \
# asmthumb.c \
# asmxtensa.c \
# emitinlinethumb.c \
# emitinlinextensa.c \
# emitnarm.c \
# emitnthumb.c \
# emitnxtensa.c \
# emitnxtensawin.c)
ifeq ($(USE_LWIP),1)
LIB_SRC += extmod/modlwip.c \
lib/netutils/netutils.c
CFLAGS += -I$(LWIP_ROOT)/src/include \
-I$(LWIP_ROOT)/src/include/Win32-hdr \
-I$(LWIP_ROOT)/contrib \
-I$(LWIP_ROOT)/src/include/compat/posix \
-DMICROPY_PY_LWIP=1 \
-DMICROPY_PY_LWIP_SOCK_RAW=1 \
-DMICROPY_ENABLE_SCHEDULER=1 \
-DMICROPY_HAVE_SYS_ARCH_PROTECT=1 \
-DMICROPY_HAVE_SYS_ARCH_UNPROTECT=1 \
-DMICROPY_LWIP_HAVE_SIO=1 \
-DMICROPY_PY_SOCKET=1 \
-DMICROPY_PY_USOCKET=1 \
-DMICROPY_PY_USELECT_POSIX=1 \
# -DMICROPY_PY_LWIP_SLIP=1
ifeq ($(USE_FOREIGN_DLLs),1)
CFLAGS += -DLWIP_DYNAMIC
LWIP_LIBS = $(LWIP_ROOT)/lwip32_imp.lib \
$(PCAP_ROOT)/wpcap.lib
else
LWIP_LIBS = $(LWIP_ROOT)/lwip32.lib \
$(PCAP_ROOT)/wpcap.lib \
$(WINPCAP_ROOT)/lib/Packet.lib
endif
LWIP_LIBS += advapi32.lib
endif
ifeq ($(USE_MBEDTLS),1)
CFLAGS += -I$(MBEDTLS_ROOT)/include \
-DMICROPY_PY_USSL=1 \
-DMICROPY_PY_UHASHLIB_MD5=1 \
-DMICROPY_PY_UHASHLIB_SHA1=1 \
-DMICROPY_PY_UCRYPTOLIB=1 \
-DMICROPY_SSL_MBEDTLS=1
LIB_SRC += extmod/modussl_mbedtls.c
ifeq ($(USE_FOREIGN_DLLs),1)
MBEDTLS_LIBS = $(addprefix $(MBEDTLS_ROOT)/library/, \
mbedcrypto_imp.lib \
mbedtls_imp.lib \
mbedx509_imp.lib)
else
MBEDTLS_LIBS = $(addprefix $(MBEDTLS_ROOT)/library/, \
mbedcrypto.lib \
mbedtls.lib \
mbedx509.lib) \
advapi32.lib
endif
endif
ifeq ($(USE_HELP_MODULES),1)
CFLAGS += -DMICROPY_PY_BUILTINS_HELP=1 \
-DMICROPY_PY_BUILTINS_HELP_MODULES=1
endif
#
# Does not work:
#
VPATH += examples/embedding
EXAMPLES_SRC = $(addprefix examples/, \
embedding/hello-embed.c)
LIB_OBJ = $(call c_to_obj, $(LIB_SRC))
EXAMPLES_OBJ = $(call c_to_obj, $(EXAMPLES_SRC))
EXAMPLES = $(notdir $(EXAMPLES_OBJ:.obj=.exe))
#
# The libraries for example programs: (not yet)
#
PROG_INT_LIB = $(STAT_LIB)
PROG_EXT_LIBS = $(LWIP_LIBS)
all: mk_dirs $(GENERATED) $(TARGETS) epilogue
epilogue:
$(call green_msg, Welcome to MicroPython (VERSION=$(VERSION), CC=$(CC)).)
$(call green_msg, You could do a $(BRIGHT_YELLOW)make -f Makefile.Windows CC=$(CC) examples$(BRIGHT_GREEN) too.)
mk_dirs:
- @mkdir --parents $(OBJ_DIR)/genhdr/qstr/ports/windows/msvc
- @mkdir $(OBJ_DIR)/genhdr/qstr/extmod \
$(OBJ_DIR)/genhdr/qstr/lib \
$(OBJ_DIR)/genhdr/qstr/lib/embed \
$(OBJ_DIR)/genhdr/qstr/lib/mp-readline \
$(OBJ_DIR)/genhdr/qstr/lib/netutils \
$(OBJ_DIR)/genhdr/qstr/lib/oofatfs \
$(OBJ_DIR)/genhdr/qstr/lib/timeutils \
$(OBJ_DIR)/genhdr/qstr/lib/utils \
$(OBJ_DIR)/genhdr/qstr/ports/unix \
$(OBJ_DIR)/genhdr/qstr/py
examples: $(EXAMPLES)
$(IMP_LIB): uPython.dll
uPython.dll: $(OBJ_DIR)/uPython.def $(OBJ_DIR)/uPython_dll.res $(LIB_OBJ)
$(call link_DLL, $@, $(IMP_LIB), -def:$^ $(LWIP_LIBS) $(MBEDTLS_LIBS))
ifeq ($(USE_uPython_DLL),1)
MicroPython.exe: $(OBJ_DIR)/main.obj $(OBJ_DIR)/MicroPython_exe.res $(IMP_LIB)
$(call link_EXE, $@, $^)
else
MicroPython.exe: $(OBJ_DIR)/main.obj $(OBJ_DIR)/MicroPython_exe.res $(STAT_LIB)
$(call link_EXE, $@, $^ $(LWIP_LIBS) $(MBEDTLS_LIBS))
endif
$(OBJ_DIR)/uPython.def: $(STAT_LIB) $(THIS_FILE)
$(call make_def_file, $@, $<)
$(STAT_LIB): $(LIB_OBJ)
$(call create_lib, $@, $^)
$(CC).args: $(THIS_FILE)
$(call yellow_msg, All CFLAGS are in $@)
$(call create_resp_file, $@, -c $(CFLAGS))
@echo
$(OBJ_DIR)/genhdr/moduledefs.h: $(LIB_SRC)
$(call Generate, $@, //)
$(PYTHON) py/makemoduledefs.py $(LIB_SRC) >> $@
@echo
#
# Generate the list of 'QDEF()' from the preprocessed and collected 'Q()' list
#
$(OBJ_DIR)/genhdr/qstrdefs.generated.h: $(OBJ_DIR)/genhdr/qstrdefs.preprocessed.h $(OBJ_DIR)/genhdr/qstrdefs.collected.h
$(call Generate, $@, //)
$(PYTHON) py/makeqstrdata.py $^ >> $@
@echo
$(OBJ_DIR)/genhdr/qstrdefs.collected.h: $(OBJ_DIR)/genhdr/qstr.i.last $(THIS_FILE)
$(call green_msg, Creating $@ to extract all 'MP_QSTRxx' from $<...)
$(PYTHON) py/makeqstrdefs.py split qstr $< $(OBJ_DIR)/genhdr/qstr _
$(PYTHON) py/makeqstrdefs.py cat qstr _ $(OBJ_DIR)/genhdr/qstr [email protected]
sort [email protected] | uniq > $@
@echo
$(OBJ_DIR)/genhdr/qstrdefs.preprocessed.h: py/qstrdefs.h ports/unix/qstrdefsport.h
$(call make_pp_file, $@, $^)
$(OBJ_DIR)/genhdr/qstr.i.last: $(Preprocessed_files) $(THIS_FILE)
$(call green_msg, Creating $@ to extract all 'MP_QSTRxx' from $$(Preprocessed_files)...)
grep --no-filename -P 'MP_QSTR|#' $(Preprocessed_files) > $@
@echo
$(OBJ_DIR)/genhdr/mpversion.h: py/makeversionhdr.py $(THIS_FILE)
$(call green_msg, Creating $@)
$(PYTHON) $< $@
$(OBJ_DIR)/genhdr/qstr/py/%.pp: py/%.c | $(CC).args
$(call make_pp_file, $@, $<)
$(OBJ_DIR)/genhdr/qstr/extmod/%.pp: extmod/%.c | $(CC).args
$(call make_pp_file, $@, $<)
$(OBJ_DIR)/genhdr/qstr/lib/embed/%.pp: lib/embed/%.c | $(CC).args
$(call make_pp_file, $@, $<)
$(OBJ_DIR)/genhdr/qstr/lib/mp-readline/%.pp: lib/mp-readline/%.c | $(CC).args
$(call make_pp_file, $@, $<)
$(OBJ_DIR)/genhdr/qstr/lib/netutils/%.pp: lib/netutils/%.c | $(CC).args
$(call make_pp_file, $@, $<)
$(OBJ_DIR)/genhdr/qstr/lib/oofatfs/%.pp: lib/oofatfs/%.c | $(CC).args
$(call make_pp_file, $@, $<)
$(OBJ_DIR)/genhdr/qstr/lib/timeutils/%.pp: lib/timeutils/%.c | $(CC).args
$(call make_pp_file, $@, $<)
$(OBJ_DIR)/genhdr/qstr/lib/utils/%.pp: lib/utils/%.c | $(CC).args
$(call make_pp_file, $@, $<)
$(OBJ_DIR)/genhdr/qstr/ports/windows/%.pp: ports/windows/%.c | $(CC).args
$(call make_pp_file, $@, $<)
$(OBJ_DIR)/genhdr/qstr/ports/unix/%.pp: ports/unix/%.c | $(CC).args
$(call make_pp_file, $@, $<)
ifeq ($(USE_MP_COMPILE),1)
SRC_chunk_1 = $(wordlist 1, 60, $(LIB_SRC))
SRC_chunk_2 = $(wordlist 61, 120, $(LIB_SRC))
SRC_chunk_3 = $(wordlist 121, 180, $(LIB_SRC))
OBJ_chunk_1 = $(call c_to_obj, $(SRC_chunk_1))
OBJ_chunk_2 = $(call c_to_obj, $(SRC_chunk_2))
OBJ_chunk_3 = $(call c_to_obj, $(SRC_chunk_3))
$(OBJ_chunk_1): $(SRC_chunk_1)
$(call Compile_MP, $(SRC_chunk_1))
$(OBJ_chunk_2): $(SRC_chunk_2)
$(call Compile_MP, $(SRC_chunk_2))
$(OBJ_chunk_3): $(SRC_chunk_3)
$(call Compile_MP, $(SRC_chunk_3))
endif
#
# All sources in 'examples/embedding/*.c' use this rule and
# the './examples/embedding/mpconfigport_minimal.h' config-file.
#
hello_CFLAGS = -I./examples/embedding \
-DNO_QSTR \
-DMP_ENDIANNESS_LITTLE=1 \
-DNORETURN=__declspec(noreturn) \
-DMICROPY_WARNINGS=1 \
-DMP_CONFIGFILE=\"./examples/embedding/mpconfigport_minimal.h\"
hello-$(CC).args: $(THIS_FILE)
$(call yellow_msg, All CFLAGS for 'examples/embedding/*.c' are in $@)
$(call create_resp_file, $@, -c $(CFLAGS) $(hello_CFLAGS))
@echo
hello-hacks.c: $(THIS_FILE)
$(call Generate, $@, //)
$(file >> $@, $(hello-hacks_c))
$(OBJ_DIR)/hello-%.obj: %.c | hello-$(CC).args hello-hacks.c
$(strip $(CC) @hello-$(CC).args) -Fo./$@ $<
@echo
$(OBJ_DIR)/%.obj: %.c | $(CC).args
$(strip $(CC) $(EXTRA_CFLAGS) @$(CC).args) -Fo./$@ $<
@echo
$(OBJ_DIR)/%.res: $(OBJ_DIR)/%.rc
$(call make_res_file, $@, $<)
hello-embed.exe: $(OBJ_DIR)/hello-embed.obj $(OBJ_DIR)/hello-hacks.obj $(STAT_LIB)
$(call link_EXE, $@, $^ $(LWIP_LIBS) $(MBEDTLS_LIBS))
#
# For .c-files in VPATH:
#
%.i: %.c $(GENERATED) cpp_filter.py $(CC).args FORCE
$(call C_preprocess, $<, $@)
FORCE:
cpp_filter.py: $(THIS_FILE)
$(call Generate, $@, #)
$(file > $@,#!/usr/env/python)
$(file >> $@,#)
$(file >> $@,# DO NOT EDIT! This file was generated automatically from)
$(file >> $@,# $(realpath $(THIS_FILE)) at $(TODAY).)
$(file >> $@,#)
$(file >> $@,from __future__ import print_function)
$(file >> $@,if 1:)
$(file >> $@,$(cpp_filter_py))
ifeq ($(USE_LWIP),1)
RC_EXTRA_DESCRIPTION =, LwIP-support
endif
ifeq ($(USE_MBEDTLS),1)
RC_EXTRA_DESCRIPTION +=, MbedTLS-support
endif
$(OBJ_DIR)/uPython_dll.rc: $(MDEPEND)
$(call Generate, $@, //)
$(file >> $@, #define RC_FILENAME "uPython.dll")
$(file >> $@, #define RC_FILETYPE VFT_DLL)
$(file >> $@, #define RC_EXTRA_DESCRIPTION "$(strip $(RC_EXTRA_DESCRIPTION)).")
$(file >> $@, $(MicroPython_rc))
$(OBJ_DIR)/MicroPython_exe.rc: $(MDEPEND)
$(call Generate, $@, //)
$(file >> $@, #define RC_FILENAME "MicroPython.exe")
$(file >> $@, #define RC_FILETYPE VFT_APP)
$(file >> $@, #define RC_EXTRA_DESCRIPTION "$(strip $(RC_EXTRA_DESCRIPTION)).")
$(file >> $@, $(MicroPython_rc))
OTHER_JUNK = .depend.Windows \
vc1*.pdb \
cpp_filter.py \
lib.args \
link.args \
link.tmp \
cl.args \
clang-cl.args \
hello-*.args \
hello-hacks.c \
MicroPython.map \
MicroPython.pdb \
MicroPython_imp.lib \
MicroPython_imp.exp
clean:
rm -fR $(OBJ_DIR)/*
rm -f $(OTHER_JUNK)
- rmdir $(OBJ_DIR)
vclean realclean: clean
- rm -f $(TARGETS) $(EXAMPLES) $(EXAMPLES:.exe=.map) $(EXAMPLES:.exe=.pdb)
#
# GNU-make macros:
#
# The following ESC-codes assumes you have MSys/Cygwin's echo 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)))
white_msg = $(call colour_msg,$(BRIGHT_WHITE)$(strip $(1)))
define Warning
$(1)
$(1) DO NOT EDIT! This file was automatically generated
$(1) from $(realpath $(THIS_FILE)) at $(TODAY).
$(1) Edit that file instead.
$(1)
endef
define Generate
$(call green_msg, Generating $(1))
$(file > $(1),$(call Warning,$(2)))
endef
define Compile_MP
$(call green_msg, Compiling $(words $(1)) files in one go...)
$(CC) @$(CC).args -MP -Fo./$(OBJ_DIR)\\ $(1)
@echo
endef
#
# For C-preprocessing.
#
C_preprocess = $(strip $(CC) -E $(EXTRA_CFLAGS) @$(CC).args $(1)) | $(PYTHON) cpp_filter.py
ifeq ($(USE_CLANG_FORMATER),1)
C_preprocess += | clang-format -style=Mozilla -assume-filename=c > $(2)
else ifeq ($(USE_GNU_INDENT),1)
C_preprocess += | indent -st > $(2)
else
C_preprocess += > $(2)
endif
#
# Create a response file $(1).
# One word from $(2) per line.
#
define create_resp_file
$(file > $(1))
$(foreach f, $(2), $(file >> $(1),$(strip $(f))) )
endef
#
# Create an EXE from objects:
# arg1, $(1): The .exe file.
# arg2, $(2): The .obj file(s), extra args and libs.
#
define link_EXE
$(call green_msg, Linking $(1))
$(call create_resp_file, link.args, $(LDFLAGS) $(2))
link -out:$(strip $(1)) @link.args > link.tmp
@cat link.tmp >> $(1:.exe=.map)
@rm -f link.tmp
$(call white_msg, Done.\n)
endef
#
# Create a DLL + import-lib from objects .obj:
# arg1, $(1): The .dll file.
# arg2, $(2): The import-lib.
# arg3, $(3): The .obj file(s), extra args and libs.
#
define link_DLL
$(call green_msg, Linking $(1))
$(call create_resp_file, link.args, $(LDFLAGS) -dll -implib:$(strip $(2)) $(3))
link -out:$(strip $(1)) @link.args > link.tmp
@cat link.tmp >> $(1:.dll=.map)
@rm -f $(2:.lib=.exp) link.tmp
$(call white_msg, Done.\n)
endef
#
# Create a static library.
#
define create_lib
$(call green_msg, Creating static lib $(1))
rm -f $(1)
$(call create_resp_file, lib.args, $(2))
lib -nologo -out:$(strip $(1)) @lib.args
@echo
endef
#
# Create a .res-file:
# arg1: $(1): the name of the .res-file.
# arg2: $(2): the .rc-file.
#
define make_res_file
rc $(RCFLAGS) -Fo./$(strip $(1)) $(2)
@echo
endef
#
# Preprocess a .c-file for all the 'Q(xx)' definitions
#
define make_pp_file
$(call green_msg, Creating $(1) by preprocessing$(BRIGHT_WHITE)$(2).)
$(CC) @$(CC).args -DNO_QSTR -E $(2) > $(1)
@echo
endef
#
# Create a .def-file: For 'x86' only
# arg1: $(1): the name of the .def-file.
# arg2: $(2): the .obj-files or library to extract symbols from.
#
filter_symbols = sed -e 's/^.* _/ /' \
-e 's/console_sighandler@4//' # 'stdcalls' are not right. Why?
extract_symbols = nm $(1) | grep -e ' T _.*' | sed -e 's/^ T _/ /'| $(filter_symbols)
extract_symbols = nm $(1) | grep -e ' T _mp_.*' | sed -e 's/^ T _/ /' | sed -e 's/^.* _/ /'
define make_def_file
$(call Generate, $(1), ;)
$(file >> $(1), LIBRARY $(notdir $(1:.def=.dll)))
$(file >> $(1), EXPORTS)
$(call extract_symbols, $(2)) | sort | uniq >> $(1)
endef
ifeq ($(USER),gv)
RC_PRIVATE_BUILD = "Private build of <[email protected]>."
endif
define MicroPython_rc
#include <winver.h>
#if defined(__clang__)
#define RC_HOST "clang"
#elif defined(_MSC_VER)
#define RC_HOST "MSVC"
#else
#error "Unknown 'RC_HOST'."
#endif
#if !defined(RC_FILENAME)
#error "define RC_FILENAME first"
#endif
#if !defined(RC_FILETYPE)
#error "define RC_FILETYPE first"
#endif
#if !defined(RC_EXTRA_DESCRIPTION)
#define RC_EXTRA_DESCRIPTION ""
#endif
#define RC_VERSION $(VER_MAJOR),$(VER_MINOR),$(VER_MICRO),0
#define RC_DESCRIPTION "A lean and efficient Python implementation"
//APPICON ICON "MicroPython.ico" /** \todo Create and add a nice icon. */
VS_VERSION_INFO VERSIONINFO
FILEVERSION RC_VERSION
PRODUCTVERSION RC_VERSION
FILEFLAGSMASK 0x3fL
FILEFLAGS 0
FILEOS VOS__WINDOWS32
FILETYPE RC_FILETYPE
FILESUBTYPE 0
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "MicroPython, https://github.com/micropython/micropython"
VALUE "FileDescription", RC_DESCRIPTION RC_EXTRA_DESCRIPTION " (" RC_HOST ", x86)"
VALUE "FileVersion", "$(VERSION)"
VALUE "InternalName", "MicroPython"
VALUE "OriginalFilename", RC_FILENAME
VALUE "ProductName", RC_FILENAME
VALUE "ProductVersion", "$(VERSION)"
VALUE "LegalCopyright", "Copyright (c) 2013-2020 Damien P. George. MIT License"
VALUE "PrivateBuild", $(RC_PRIVATE_BUILD)
VALUE "Comments", "Built on $(TODAY)."
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
endef
define hello-hacks_c
#include <stdio.h>
#include <unistd.h>
#include "py/mperrno.h"
#include "py/obj.h"
static void stderr_print_strn (void *env, const char *str, size_t len)
{
(void) env;
write (STDERR_FILENO, str, len);
}
const mp_print_t mp_stderr_print = { NULL, stderr_print_strn };
mp_obj_t mp_builtin_open (size_t n_args, const mp_obj_t *args, mp_map_t *kwargs)
{
mp_raise_OSError (MP_EPERM);
}
MP_DEFINE_CONST_FUN_OBJ_KW (mp_builtin_open_obj, 1, mp_builtin_open);
endef
define cpp_filter_py
#
# A tool used in "$$(CC) -E" rules to remove empty
# preproessor lines and make the output a bit more readable.
#
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 ('\\', '/')
def skip_cwd (s1, s2):
''' Skip the leading part that is in common with s1 and s2
'''
i = 0
while i < len(s1) and s1[i] == s2[i]:
i += 1
return s2[i:]
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')) and last_line[0] == '\r':
empty_lines += 1
continue
if line.lstrip().startswith("#line"):
line = line.replace ("\\\\", "/")
fname = None
quote = line.find ('\"')
if line.startswith("#line ") and quote > 0:
fname = _win32_abspath (line[quote:])
last_fname = fname
line = line [:quote] + skip_cwd (cwd, fname)
if line.strip() != '' and last_line != '':
if fname is None or fname != last_fname:
print (line, end="")
last_line = line
if empty_lines > 0:
sys.stderr.write ("Removed %d empty lines." % empty_lines)
endef
DEP_REPLACE = sed -e 's@\(.*\)\.o: @\n$$(OBJ_DIR)/\1\.obj: @' \
-e 's@$(LWIP_ROOT)@$$(LWIP_ROOT)@' \
-e 's@$(MBEDTLS_ROOT)@$$(MBEDTLS_ROOT)@'
DEP_SOURCES = $(LIB_SRC) ports/windows/main.c \
$(EXAMPLES_SRC)
#
# The ouput gets filtered to *not* include some of the genearted files.
# That would cause GNU-make to do too much work.
#
DEP_REPLACE += -e 's@$(OBJ_DIR)/genhdr/qstrdefs.generated.h@@g'
depend: mk_dirs $(GENERATED)
$(call green_msg, Generating dependencies for $(words $(DEP_SOURCES)) source files...)
$(file > .depend.Windows,$(call Warning,#))
$(call create_resp_file, $(CC).args, -MM $(filter -I% -D%, $(CFLAGS)))
gcc @$(CC).args $(DEP_SOURCES) | $(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