mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 03:32:29 +00:00 
			
		
		
		
	Merge pull request #869 from cr1901/win-shell
Install launcher executable when running yosys-smtbmc on Windows.
This commit is contained in:
		
						commit
						53b28b3f01
					
				
					 3 changed files with 377 additions and 1 deletions
				
			
		
							
								
								
									
										2
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -24,6 +24,8 @@ | ||||||
| /yosys-abc.exe | /yosys-abc.exe | ||||||
| /yosys-config | /yosys-config | ||||||
| /yosys-smtbmc | /yosys-smtbmc | ||||||
|  | /yosys-smtbmc.exe | ||||||
|  | /yosys-smtbmc-script.py | ||||||
| /yosys-filterlib | /yosys-filterlib | ||||||
| /yosys-filterlib.exe | /yosys-filterlib.exe | ||||||
| /kernel/version_*.cc | /kernel/version_*.cc | ||||||
|  |  | ||||||
|  | @ -3,14 +3,30 @@ OBJS += backends/smt2/smt2.o | ||||||
| 
 | 
 | ||||||
| ifneq ($(CONFIG),mxe) | ifneq ($(CONFIG),mxe) | ||||||
| ifneq ($(CONFIG),emcc) | ifneq ($(CONFIG),emcc) | ||||||
|  | 
 | ||||||
|  | # MSYS targets support yosys-smtbmc, but require a launcher script
 | ||||||
|  | ifeq ($(CONFIG),$(filter $(CONFIG),msys2 msys2-64)) | ||||||
|  | TARGETS += yosys-smtbmc.exe yosys-smtbmc-script.py | ||||||
|  | # Needed to find the Python interpreter for yosys-smtbmc scripts.
 | ||||||
|  | # Override if necessary, it is only used for msys2 targets.
 | ||||||
|  | PYTHON := $(shell cygpath -w -m $(PREFIX)/bin/python3) | ||||||
|  | 
 | ||||||
|  | yosys-smtbmc-script.py: backends/smt2/smtbmc.py | ||||||
|  | 	$(P) sed -e 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' \ | ||||||
|  | 		-e "s|#!/usr/bin/env python3|#!$(PYTHON)|" < $< > $@ | ||||||
|  | 
 | ||||||
|  | yosys-smtbmc.exe: misc/launcher.c yosys-smtbmc-script.py | ||||||
|  | 	$(P) gcc -DGUI=0 -O -s -o $@ $< | ||||||
|  | # Other targets
 | ||||||
|  | else | ||||||
| TARGETS += yosys-smtbmc | TARGETS += yosys-smtbmc | ||||||
| 
 | 
 | ||||||
| yosys-smtbmc: backends/smt2/smtbmc.py | yosys-smtbmc: backends/smt2/smtbmc.py | ||||||
| 	$(P) sed 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' < $< > $@.new | 	$(P) sed 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' < $< > $@.new | ||||||
| 	$(Q) chmod +x $@.new | 	$(Q) chmod +x $@.new | ||||||
| 	$(Q) mv $@.new $@ | 	$(Q) mv $@.new $@ | ||||||
|  | endif | ||||||
| 
 | 
 | ||||||
| $(eval $(call add_share_file,share/python3,backends/smt2/smtio.py)) | $(eval $(call add_share_file,share/python3,backends/smt2/smtio.py)) | ||||||
| endif | endif | ||||||
| endif | endif | ||||||
| 
 |  | ||||||
|  |  | ||||||
							
								
								
									
										358
									
								
								misc/launcher.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										358
									
								
								misc/launcher.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,358 @@ | ||||||
|  | /* This file comes from the PyPA Setuptools repository, commit 16e452a:
 | ||||||
|  | https://github.com/pypa/setuptools
 | ||||||
|  | Modifications include this comment and inline inclusion of the LICENSE text. */ | ||||||
|  | 
 | ||||||
|  | /* Copyright (C) 2016 Jason R Coombs <jaraco@jaraco.com>
 | ||||||
|  | 
 | ||||||
|  | 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. */ | ||||||
|  | 
 | ||||||
|  | /*  Setuptools Script Launcher for Windows
 | ||||||
|  | 
 | ||||||
|  |     This is a stub executable for Windows that functions somewhat like | ||||||
|  |     Effbot's "exemaker", in that it runs a script with the same name but | ||||||
|  |     a .py extension, using information from a #! line.  It differs in that | ||||||
|  |     it spawns the actual Python executable, rather than attempting to | ||||||
|  |     hook into the Python DLL.  This means that the script will run with | ||||||
|  |     sys.executable set to the Python executable, where exemaker ends up with | ||||||
|  |     sys.executable pointing to itself.  (Which means it won't work if you try | ||||||
|  |     to run another Python process using sys.executable.) | ||||||
|  | 
 | ||||||
|  |     To build/rebuild with mingw32, do this in the setuptools project directory: | ||||||
|  | 
 | ||||||
|  |        gcc -DGUI=0           -mno-cygwin -O -s -o setuptools/cli.exe launcher.c | ||||||
|  |        gcc -DGUI=1 -mwindows -mno-cygwin -O -s -o setuptools/gui.exe launcher.c | ||||||
|  | 
 | ||||||
|  |     To build for Windows RT, install both Visual Studio Express for Windows 8 | ||||||
|  |     and for Windows Desktop (both freeware), create "win32" application using | ||||||
|  |     "Windows Desktop" version, create new "ARM" target via | ||||||
|  |     "Configuration Manager" menu and modify ".vcxproj" file by adding | ||||||
|  |     "<WindowsSDKDesktopARMSupport>true</WindowsSDKDesktopARMSupport>" tag | ||||||
|  |     as child of "PropertyGroup" tags that has "Debug|ARM" and "Release|ARM" | ||||||
|  |     properties. | ||||||
|  | 
 | ||||||
|  |     It links to msvcrt.dll, but this shouldn't be a problem since it doesn't | ||||||
|  |     actually run Python in the same process.  Note that using 'exec' instead | ||||||
|  |     of 'spawn' doesn't work, because on Windows this leads to the Python | ||||||
|  |     executable running in the *background*, attached to the same console | ||||||
|  |     window, meaning you get a command prompt back *before* Python even finishes | ||||||
|  |     starting.  So, we have to use spawnv() and wait for Python to exit before | ||||||
|  |     continuing.  :( | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <windows.h> | ||||||
|  | #include <tchar.h> | ||||||
|  | #include <fcntl.h> | ||||||
|  | 
 | ||||||
|  | int child_pid=0; | ||||||
|  | 
 | ||||||
|  | int fail(char *format, char *data) { | ||||||
|  |     /* Print error message to stderr and return 2 */ | ||||||
|  |     fprintf(stderr, format, data); | ||||||
|  |     return 2; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | char *quoted(char *data) { | ||||||
|  |     int i, ln = strlen(data), nb; | ||||||
|  | 
 | ||||||
|  |     /* We allocate twice as much space as needed to deal with worse-case
 | ||||||
|  |        of having to escape everything. */ | ||||||
|  |     char *result = calloc(ln*2+3, sizeof(char)); | ||||||
|  |     char *presult = result; | ||||||
|  | 
 | ||||||
|  |     *presult++ = '"'; | ||||||
|  |     for (nb=0, i=0; i < ln; i++) | ||||||
|  |       { | ||||||
|  |         if (data[i] == '\\') | ||||||
|  |           nb += 1; | ||||||
|  |         else if (data[i] == '"') | ||||||
|  |           { | ||||||
|  |             for (; nb > 0; nb--) | ||||||
|  |               *presult++ = '\\'; | ||||||
|  |             *presult++ = '\\'; | ||||||
|  |           } | ||||||
|  |         else | ||||||
|  |           nb = 0; | ||||||
|  |         *presult++ = data[i]; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |     for (; nb > 0; nb--)        /* Deal w trailing slashes */ | ||||||
|  |       *presult++ = '\\'; | ||||||
|  | 
 | ||||||
|  |     *presult++ = '"'; | ||||||
|  |     *presult++ = 0; | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | char *loadable_exe(char *exename) { | ||||||
|  |     /* HINSTANCE hPython;  DLL handle for python executable */ | ||||||
|  |     char *result; | ||||||
|  | 
 | ||||||
|  |     /* hPython = LoadLibraryEx(exename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
 | ||||||
|  |     if (!hPython) return NULL; */ | ||||||
|  | 
 | ||||||
|  |     /* Return the absolute filename for spawnv */ | ||||||
|  |     result = calloc(MAX_PATH, sizeof(char)); | ||||||
|  |     strncpy(result, exename, MAX_PATH); | ||||||
|  |     /*if (result) GetModuleFileNameA(hPython, result, MAX_PATH);
 | ||||||
|  | 
 | ||||||
|  |     FreeLibrary(hPython); */ | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | char *find_exe(char *exename, char *script) { | ||||||
|  |     char drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME], ext[_MAX_EXT]; | ||||||
|  |     char path[_MAX_PATH], c, *result; | ||||||
|  | 
 | ||||||
|  |     /* convert slashes to backslashes for uniform search below */ | ||||||
|  |     result = exename; | ||||||
|  |     while (c = *result++) if (c=='/') result[-1] = '\\'; | ||||||
|  | 
 | ||||||
|  |     _splitpath(exename, drive, dir, fname, ext); | ||||||
|  |     if (drive[0] || dir[0]=='\\') { | ||||||
|  |         return loadable_exe(exename);   /* absolute path, use directly */ | ||||||
|  |     } | ||||||
|  |     /* Use the script's parent directory, which should be the Python home
 | ||||||
|  |        (This should only be used for bdist_wininst-installed scripts, because | ||||||
|  |         easy_install-ed scripts use the absolute path to python[w].exe | ||||||
|  |     */ | ||||||
|  |     _splitpath(script, drive, dir, fname, ext); | ||||||
|  |     result = dir + strlen(dir) -1; | ||||||
|  |     if (*result == '\\') result--; | ||||||
|  |     while (*result != '\\' && result>=dir) *result-- = 0; | ||||||
|  |     _makepath(path, drive, dir, exename, NULL); | ||||||
|  |     return loadable_exe(path); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | char **parse_argv(char *cmdline, int *argc) | ||||||
|  | { | ||||||
|  |     /* Parse a command line in-place using MS C rules */ | ||||||
|  | 
 | ||||||
|  |     char **result = calloc(strlen(cmdline), sizeof(char *)); | ||||||
|  |     char *output = cmdline; | ||||||
|  |     char c; | ||||||
|  |     int nb = 0; | ||||||
|  |     int iq = 0; | ||||||
|  |     *argc = 0; | ||||||
|  | 
 | ||||||
|  |     result[0] = output; | ||||||
|  |     while (isspace(*cmdline)) cmdline++;   /* skip leading spaces */ | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         c = *cmdline++; | ||||||
|  |         if (!c || (isspace(c) && !iq)) { | ||||||
|  |             while (nb) {*output++ = '\\'; nb--; } | ||||||
|  |             *output++ = 0; | ||||||
|  |             result[++*argc] = output; | ||||||
|  |             if (!c) return result; | ||||||
|  |             while (isspace(*cmdline)) cmdline++;  /* skip leading spaces */ | ||||||
|  |             if (!*cmdline) return result;  /* avoid empty arg if trailing ws */ | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         if (c == '\\') | ||||||
|  |             ++nb;   /* count \'s */ | ||||||
|  |         else { | ||||||
|  |             if (c == '"') { | ||||||
|  |                 if (!(nb & 1)) { iq = !iq; c = 0; }  /* skip " unless odd # of \ */ | ||||||
|  |                 nb = nb >> 1;   /* cut \'s in half */ | ||||||
|  |             } | ||||||
|  |             while (nb) {*output++ = '\\'; nb--; } | ||||||
|  |             if (c) *output++ = c; | ||||||
|  |         } | ||||||
|  |     } while (1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void pass_control_to_child(DWORD control_type) { | ||||||
|  |     /*
 | ||||||
|  |      * distribute-issue207 | ||||||
|  |      * passes the control event to child process (Python) | ||||||
|  |      */ | ||||||
|  |     if (!child_pid) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     GenerateConsoleCtrlEvent(child_pid,0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | BOOL control_handler(DWORD control_type) { | ||||||
|  |     /*
 | ||||||
|  |      * distribute-issue207 | ||||||
|  |      * control event handler callback function | ||||||
|  |      */ | ||||||
|  |     switch (control_type) { | ||||||
|  |         case CTRL_C_EVENT: | ||||||
|  |             pass_control_to_child(0); | ||||||
|  |             break; | ||||||
|  |     } | ||||||
|  |     return TRUE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int create_and_wait_for_subprocess(char* command) { | ||||||
|  |     /*
 | ||||||
|  |      * distribute-issue207 | ||||||
|  |      * launches child process (Python) | ||||||
|  |      */ | ||||||
|  |     DWORD return_value = 0; | ||||||
|  |     LPSTR commandline = command; | ||||||
|  |     STARTUPINFOA s_info; | ||||||
|  |     PROCESS_INFORMATION p_info; | ||||||
|  |     ZeroMemory(&p_info, sizeof(p_info)); | ||||||
|  |     ZeroMemory(&s_info, sizeof(s_info)); | ||||||
|  |     s_info.cb = sizeof(STARTUPINFO); | ||||||
|  |     // set-up control handler callback funciotn
 | ||||||
|  |     SetConsoleCtrlHandler((PHANDLER_ROUTINE) control_handler, TRUE); | ||||||
|  |     if (!CreateProcessA(NULL, commandline, NULL, NULL, TRUE, 0, NULL, NULL, &s_info, &p_info)) { | ||||||
|  |         fprintf(stderr, "failed to create process.\n"); | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |     child_pid = p_info.dwProcessId; | ||||||
|  |     // wait for Python to exit
 | ||||||
|  |     WaitForSingleObject(p_info.hProcess, INFINITE); | ||||||
|  |     if (!GetExitCodeProcess(p_info.hProcess, &return_value)) { | ||||||
|  |         fprintf(stderr, "failed to get exit code from process.\n"); | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |     return return_value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | char* join_executable_and_args(char *executable, char **args, int argc) | ||||||
|  | { | ||||||
|  |     /*
 | ||||||
|  |      * distribute-issue207 | ||||||
|  |      * CreateProcess needs a long string of the executable and command-line arguments, | ||||||
|  |      * so we need to convert it from the args that was built | ||||||
|  |      */ | ||||||
|  |     int len,counter; | ||||||
|  |     char* cmdline; | ||||||
|  | 
 | ||||||
|  |     len=strlen(executable)+2; | ||||||
|  |     for (counter=1; counter<argc; counter++) { | ||||||
|  |         len+=strlen(args[counter])+1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     cmdline = (char*)calloc(len, sizeof(char)); | ||||||
|  |     sprintf(cmdline, "%s", executable); | ||||||
|  |     len=strlen(executable); | ||||||
|  |     for (counter=1; counter<argc; counter++) { | ||||||
|  |         sprintf(cmdline+len, " %s", args[counter]); | ||||||
|  |         len+=strlen(args[counter])+1; | ||||||
|  |     } | ||||||
|  |     return cmdline; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int run(int argc, char **argv, int is_gui) { | ||||||
|  | 
 | ||||||
|  |     char python[256];   /* python executable's filename*/ | ||||||
|  |     char *pyopt;        /* Python option */ | ||||||
|  |     char script[256];   /* the script's filename */ | ||||||
|  | 
 | ||||||
|  |     int scriptf;        /* file descriptor for script file */ | ||||||
|  | 
 | ||||||
|  |     char **newargs, **newargsp, **parsedargs; /* argument array for exec */ | ||||||
|  |     char *ptr, *end;    /* working pointers for string manipulation */ | ||||||
|  |     char *cmdline; | ||||||
|  |     int i, parsedargc;              /* loop counter */ | ||||||
|  | 
 | ||||||
|  |     /* compute script name from our .exe name*/ | ||||||
|  |     GetModuleFileNameA(NULL, script, sizeof(script)); | ||||||
|  |     end = script + strlen(script); | ||||||
|  |     while( end>script && *end != '.') | ||||||
|  |         *end-- = '\0'; | ||||||
|  |     *end-- = '\0'; | ||||||
|  |     strcat(script, (GUI ? "-script.pyw" : "-script.py")); | ||||||
|  | 
 | ||||||
|  |     /* figure out the target python executable */ | ||||||
|  | 
 | ||||||
|  |     scriptf = open(script, O_RDONLY); | ||||||
|  |     if (scriptf == -1) { | ||||||
|  |         return fail("Cannot open %s\n", script); | ||||||
|  |     } | ||||||
|  |     end = python + read(scriptf, python, sizeof(python)); | ||||||
|  |     close(scriptf); | ||||||
|  | 
 | ||||||
|  |     ptr = python-1; | ||||||
|  |     while(++ptr < end && *ptr && *ptr!='\n' && *ptr!='\r') {;} | ||||||
|  | 
 | ||||||
|  |     *ptr-- = '\0'; | ||||||
|  | 
 | ||||||
|  |     if (strncmp(python, "#!", 2)) { | ||||||
|  |         /* default to python.exe if no #! header */ | ||||||
|  |         strcpy(python, "#!python.exe"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     parsedargs = parse_argv(python+2, &parsedargc); | ||||||
|  | 
 | ||||||
|  |     /* Using spawnv() can fail strangely if you e.g. find the Cygwin
 | ||||||
|  |        Python, so we'll make sure Windows can find and load it */ | ||||||
|  | 
 | ||||||
|  |     ptr = find_exe(parsedargs[0], script); | ||||||
|  |     if (!ptr) { | ||||||
|  |         return fail("Cannot find Python executable %s\n", parsedargs[0]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* printf("Python executable: %s\n", ptr); */ | ||||||
|  | 
 | ||||||
|  |     /* Argument array needs to be
 | ||||||
|  |        parsedargc + argc, plus 1 for null sentinel */ | ||||||
|  | 
 | ||||||
|  |     newargs = (char **)calloc(parsedargc + argc + 1, sizeof(char *)); | ||||||
|  |     newargsp = newargs; | ||||||
|  | 
 | ||||||
|  |     *newargsp++ = quoted(ptr); | ||||||
|  |     for (i = 1; i<parsedargc; i++) *newargsp++ = quoted(parsedargs[i]); | ||||||
|  | 
 | ||||||
|  |     *newargsp++ = quoted(script); | ||||||
|  |     for (i = 1; i < argc; i++)     *newargsp++ = quoted(argv[i]); | ||||||
|  | 
 | ||||||
|  |     *newargsp++ = NULL; | ||||||
|  | 
 | ||||||
|  |     /* printf("args 0: %s\nargs 1: %s\n", newargs[0], newargs[1]); */ | ||||||
|  | 
 | ||||||
|  |     if (is_gui) { | ||||||
|  |         /* Use exec, we don't need to wait for the GUI to finish */ | ||||||
|  |         execv(ptr, (const char * const *)(newargs)); | ||||||
|  |         return fail("Could not exec %s", ptr);   /* shouldn't get here! */ | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /*
 | ||||||
|  |      * distribute-issue207: using CreateProcessA instead of spawnv | ||||||
|  |      */ | ||||||
|  |     cmdline = join_executable_and_args(ptr, newargs, parsedargc + argc); | ||||||
|  |     return create_and_wait_for_subprocess(cmdline); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR lpCmd, int nShow) { | ||||||
|  |     return run(__argc, __argv, GUI); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int main(int argc, char** argv) { | ||||||
|  |     return run(argc, argv, GUI); | ||||||
|  | } | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue