Fix for pr1833776.
[iverilog.git] / driver-vpi / main.c
blobdf325f8e9c6565e1b796b00e8e728dbd27e311d6
1 /*
2 * Copyright (c) 2002 Gus Baldauf (gus@picturel.com)
4 * This source code is free software; you can redistribute it
5 * and/or modify it in source code form under the terms of the GNU
6 * General Public License as published by the Free Software
7 * Foundation; either version 2 of the License, or (at your option)
8 * any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
21 * iverilog-vpi.c
23 * this program provides the functionality of iverilog-vpi.sh under Win32
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
33 #include <windows.h>
35 /* Macros used for compiling and linking */
37 #define IVERILOG_VPI_CC "gcc" /* no .exe extension */
38 #define IVERILOG_VPI_CXX "gcc" /* no .exe extension */
39 #define IVERILOG_VPI_CFLAGS "-O" /* -I appended later */
40 #define IVERILOG_VPI_LD "gcc" /* no .exe extension */
41 #define IVERILOG_VPI_LDFLAGS "-shared -Wl,--enable-auto-image-base"
42 #define IVERILOG_VPI_LDLIBS "-lveriuser -lvpi" /* -L prepended later */
44 /* pointers to global strings */
46 static struct global_strings {
47 char *pCCSRC; /* list of C source files */
48 char *pCXSRC; /* list of C++ source files */
49 char *pOBJ; /* list of object files */
50 char *pLIB; /* list of library files */
51 char *pOUT; /* output file name (.vpi extension), if 0 length then no source files specified */
52 char *pMINGW; /* path to MinGW directory */
53 char *pIVL; /* path to IVL directory */
54 char *pCFLAGS; /* CFLAGS option */
55 char *pLDLIBS; /* LDLIBS option */
56 char *pNewPath; /* new PATH environment variable setting */
57 } gstr;
60 static void deInitDynString(char *str)
62 free(str);
65 /* when finished, free allocated memory and return error code */
67 static void myExit(int exitVal)
69 deInitDynString(gstr.pCCSRC);
70 deInitDynString(gstr.pCXSRC);
71 deInitDynString(gstr.pOBJ);
72 deInitDynString(gstr.pLIB);
73 deInitDynString(gstr.pOUT);
74 deInitDynString(gstr.pMINGW);
75 deInitDynString(gstr.pIVL);
76 deInitDynString(gstr.pCFLAGS);
77 deInitDynString(gstr.pLDLIBS);
78 deInitDynString(gstr.pNewPath);
80 exit(exitVal);
83 /* display usage summary and exit */
85 static void usage()
87 fprintf(stderr,"usage: iverilog-vpi [--name=name] [-llibrary] [-mingw=dir] [-ivl=dir] sourcefile...\n");
88 fprintf(stderr," or iverilog-vpi -mingw=dir\n");
89 fprintf(stderr," or iverilog-vpi -ivl=dir\n");
90 myExit(1);
93 static void initDynString(char **str)
95 *str = (char *) malloc(1);
97 if (!*str) {
98 fprintf(stderr,"error: out of memory\n");
99 myExit(4);
102 *str[0] = 0;
105 /* initialize dynamic memory buffers */
107 static void init()
109 initDynString(&gstr.pCCSRC);
110 initDynString(&gstr.pCXSRC);
111 initDynString(&gstr.pOBJ);
112 initDynString(&gstr.pLIB);
113 initDynString(&gstr.pOUT);
114 initDynString(&gstr.pMINGW);
115 initDynString(&gstr.pIVL);
116 initDynString(&gstr.pCFLAGS);
117 initDynString(&gstr.pLDLIBS);
118 initDynString(&gstr.pNewPath);
121 /* return true if "str" is terminated with with "end", case insensitive */
123 static int endsIn (char *end, char *str)
125 char *ext;
127 if (strlen(end) >= strlen(str))
128 return 0;
130 ext = str + (strlen(str) - strlen(end));
132 return stricmp(end,ext) ? 0 : 1;
135 /* return true if "str" begins with "prefix", case insensitive */
137 static int startsWith (char *prefix, char *str)
139 if (strlen(prefix) >= strlen(str))
140 return 0;
142 return strnicmp(prefix,str,strlen(prefix)) ? 0 : 1;
145 /* append "app" to "ptr", allocating memory as needed */
146 /* if count is zero, then copy all characters of "app" */
148 static void appendn (char **ptr, char *app, int count)
150 *ptr = (char *) realloc(*ptr,strlen(*ptr)+(count?count:strlen(app))+1);
152 if (*ptr == NULL) {
153 fprintf(stderr,"error: out of memory\n");
154 myExit(4);
157 if (count)
158 strncat(*ptr,app,count);
159 else
160 strcat(*ptr,app);
163 /* append "app" to "ptr", allocating memory as needed */
165 static void append (char **ptr, char *app)
167 appendn(ptr,app,0);
170 /* if the string does not end with a backslash, add one */
172 static void appendBackSlash(char **str)
174 if ((*str)[strlen(*str)-1] != '\\')
175 append(str,"\\");
178 /* copy count characters of "str" to "ptr", allocating memory as needed */
179 /* if count is zero, then copy all characters of "str" */
181 static void assignn (char **ptr, char *str, int count)
183 *ptr = (char *) realloc(*ptr,(count?count:strlen(str))+1);
185 if (*ptr == NULL) {
186 fprintf(stderr,"error: out of memory\n");
187 myExit(4);
190 if (count) {
191 strncpy(*ptr,str,count);
192 (*ptr)[count] = 0;
194 else
195 strcpy(*ptr,str);
198 /* copy count characters of "str" to "ptr", allocating memory as needed */
200 static void assign (char **ptr, char *str)
202 assignn(ptr,str,0);
205 /* get a copy of a Icarus Verilog registry string key */
207 static int GetRegistryKey(char *key, char **value)
209 long lrv;
210 HKEY hkKey;
211 char *regKeyBuffer;
212 DWORD regKeyType, regKeySize;
214 lrv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,"Software\\Icarus Verilog",0,KEY_QUERY_VALUE,&hkKey);
215 if (lrv != ERROR_SUCCESS)
216 return 0;
218 lrv = RegQueryValueEx(hkKey,key,NULL,&regKeyType,NULL,&regKeySize);
219 if ((lrv != ERROR_SUCCESS) || (regKeyType != REG_SZ) || (!regKeySize)) {
220 lrv = RegCloseKey(hkKey);
221 return 0;
224 regKeyBuffer = (char *) malloc(regKeySize+1);
225 if (!regKeyBuffer) {
226 lrv = RegCloseKey(hkKey);
227 fprintf(stderr,"error: out of memory\n");
228 myExit(4);
230 regKeyBuffer[regKeySize] = 0; /* makes sure there is a trailing NULL */
232 lrv = RegQueryValueEx(hkKey,key,NULL,&regKeyType,regKeyBuffer,&regKeySize);
233 if ((lrv != ERROR_SUCCESS) || (regKeyType != REG_SZ) || (!regKeySize)) {
234 lrv = RegCloseKey(hkKey);
235 free(regKeyBuffer);
236 return 0;
239 RegCloseKey(hkKey);
241 assign(value,regKeyBuffer);
242 free(regKeyBuffer);
244 return 1;
247 /* store a copy of a Icarus Verilog registry string key */
249 static void SetRegistryKey(char *key, char *value)
251 HKEY hkKey;
252 DWORD res;
254 if (RegCreateKeyEx(
255 HKEY_LOCAL_MACHINE,
256 "Software\\Icarus Verilog",
259 REG_OPTION_NON_VOLATILE,
260 KEY_ALL_ACCESS,NULL,
261 &hkKey,
262 &res) != ERROR_SUCCESS)
263 return;
265 RegSetValueEx(hkKey,key,0,REG_SZ,value,strlen(value)+1);
266 RegCloseKey(hkKey);
268 printf("info: storing %s in Windows' registry entry\n",value);
269 printf(" HKEY_LOCAL_MACHINE\\Software\\Icarus Verilog\\%s\n",key);
272 /* parse the command line, assign results to global variable strings */
274 static int parse(int argc, char *argv[])
276 int idx, srcFileCnt=0;
278 char dot_c_ext[] = ".c";
279 char dot_cc_ext[] = ".cc";
280 char dot_cpp_ext[] = ".cpp";
281 char dot_o_ext[] = ".o";
282 char name_option[] = "--name=";
283 char lib_option[] = "-l";
284 char mingw_option[] = "-mingw=";
285 char ivl_option[] = "-ivl=";
287 if (argc == 1)
288 return 0;
290 for (idx=1; idx<argc; ++idx) {
291 if (endsIn(dot_c_ext,argv[idx])) { /* check for C source files */
292 ++srcFileCnt;
293 append(&gstr.pCCSRC,argv[idx]);
294 append(&gstr.pCCSRC," ");
295 if (!*gstr.pOUT)
296 assignn(&gstr.pOUT,argv[idx],strlen(argv[idx])-strlen(dot_c_ext));
298 else if (endsIn(dot_cc_ext,argv[idx])) { /* check for C++ source files */
299 ++srcFileCnt;
300 append(&gstr.pCXSRC,argv[idx]);
301 append(&gstr.pCXSRC," ");
302 if (!*gstr.pOUT)
303 assignn(&gstr.pOUT,argv[idx],strlen(argv[idx])-strlen(dot_cc_ext));
305 else if (endsIn(dot_cpp_ext,argv[idx])) { /* check for C++ source files */
306 ++srcFileCnt;
307 append(&gstr.pCXSRC,argv[idx]);
308 append(&gstr.pCXSRC," ");
309 if (!*gstr.pOUT)
310 assignn(&gstr.pOUT,argv[idx],strlen(argv[idx])-strlen(dot_cpp_ext));
312 else if (endsIn(dot_o_ext,argv[idx])) { /* check for compiled object files */
313 ++srcFileCnt;
314 append(&gstr.pOBJ,argv[idx]);
315 append(&gstr.pOBJ," ");
316 if (!*gstr.pOUT)
317 assignn(&gstr.pOUT,argv[idx],strlen(argv[idx])-strlen(dot_o_ext));
319 else if (startsWith(name_option,argv[idx])) { /* check for --name option */
320 assignn(&gstr.pOUT,argv[idx]+sizeof(name_option)-1,strlen(argv[idx])-(sizeof(name_option)-1));
322 else if (startsWith(lib_option,argv[idx])) { /* check for -l option */
323 append(&gstr.pLIB,argv[idx]);
324 append(&gstr.pLIB," ");
326 else if (startsWith(mingw_option,argv[idx])) /* check for -mingw option */
327 assignn(&gstr.pMINGW,argv[idx]+sizeof(mingw_option)-1,strlen(argv[idx])-(sizeof(mingw_option)-1));
328 else if (startsWith(ivl_option,argv[idx])) /* check for -ivl option */
329 assignn(&gstr.pIVL,argv[idx]+sizeof(ivl_option)-1,strlen(argv[idx])-(sizeof(ivl_option)-1));
330 else
331 return 0; /* different from iverilog-vpi.sh, we don't ignore accept arguments */
334 if (0 == srcFileCnt)
335 assign(&gstr.pOUT,""); /* in case they used --name with no source files */
337 if (!*gstr.pOUT) { /* normally it's an error if there are no *.c,*.cc,*.o files */
338 if (!*gstr.pMINGW && !*gstr.pIVL) /* unless they are just setting the IVL or MinGW registry entries */
339 usage();
341 else {
342 append(&gstr.pOUT,".vpi"); /* the result file should have a .vpi extension */
343 append(&gstr.pOUT," ");
346 return 1;
349 /* do minimal check that the MinGW root directory looks valid */
351 static void checkMingwDir(char *root)
353 int irv;
354 struct _stat stat_buf;
356 char *path;
357 initDynString(&path);
358 assign(&path,gstr.pMINGW);
359 appendBackSlash(&path);
360 append(&path,"bin\\" IVERILOG_VPI_CC ".exe");
362 irv = _stat(path,&stat_buf);
363 deInitDynString(path);
365 if (irv) {
366 fprintf(stderr,"error: %s does not appear to be the valid root directory\n",root);
367 fprintf(stderr," of MinGW. Use the -mingw option of iverilog-vpi.exe to\n");
368 fprintf(stderr," point to the MinGW root directory. For a Windows command\n");
369 fprintf(stderr," shell the option would be something like -mingw=c:\\mingw\n");
370 fprintf(stderr," For a Cygwin shell the option would be something like\n");
371 fprintf(stderr," -mingw=c:\\\\mingw\n");
372 myExit(5);
376 /* do minimal check that the Icarus Verilog root directory looks valid */
378 static void checkIvlDir(char *root)
380 int irv;
381 struct _stat stat_buf;
383 char *path;
384 initDynString(&path);
385 assign(&path,gstr.pIVL);
386 appendBackSlash(&path);
387 append(&path,"bin\\vvp.exe");
389 irv = _stat(path,&stat_buf);
390 deInitDynString(path);
392 if (irv) {
393 fprintf(stderr,"error: %s does not appear to be the valid root directory of\n",root);
394 fprintf(stderr," Icarus Verilog. Use the -ivl option of iverilog-vpi.exe to\n");
395 fprintf(stderr," point to the Icarus Verilog root directory. For a Windows\n");
396 fprintf(stderr," command shell the option would be something like -ivl=c:\\iverilog\n");
397 fprintf(stderr," For a Cygwin shell the option would be something like\n");
398 fprintf(stderr," -ivl=c:\\\\iverilog\n");
399 myExit(6);
403 /* see if we can find mingw root */
405 #define IVL_REGKEY_MINGW "MingwDir"
407 static void setup_mingw_environment()
409 char *pOldPATH = getenv("PATH"); /* get current path */
411 if (*gstr.pMINGW) {
412 checkMingwDir(gstr.pMINGW);
413 SetRegistryKey(IVL_REGKEY_MINGW,gstr.pMINGW);
415 else
416 if (!GetRegistryKey(IVL_REGKEY_MINGW,&gstr.pMINGW)) {
417 fprintf(stderr,"error: can not locate the MinGW root directory, use the -mingw option of\n");
418 fprintf(stderr," iverilog-vpi.exe to point to the MinGW root directory. For\n");
419 fprintf(stderr," a Windows command shell the option would be something like\n");
420 fprintf(stderr," -mingw=c:\\mingw For a Cygwin shell the option would be\n");
421 fprintf(stderr," something like -mingw=c:\\\\mingw\n");
422 myExit(5);
425 assign(&gstr.pNewPath,"PATH="); /* create new path */
426 append(&gstr.pNewPath,gstr.pMINGW);
427 appendBackSlash(&gstr.pNewPath);
428 append(&gstr.pNewPath,"bin;");
429 append(&gstr.pNewPath,pOldPATH);
431 _putenv(gstr.pNewPath); /* place new path in environment variable */
434 /* see if we can find iverilog root */
436 #define IVL_REGKEY_IVL "InstallDir"
438 static void setup_ivl_environment()
440 if (*gstr.pIVL) {
441 checkIvlDir(gstr.pIVL);
442 SetRegistryKey(IVL_REGKEY_IVL,gstr.pIVL);
444 else
445 if (!GetRegistryKey(IVL_REGKEY_IVL,&gstr.pIVL)) {
446 fprintf(stderr,"error: can not locate the Icarus Verilog root directory, use the -ivl option\n");
447 fprintf(stderr," of iverilog-vpi.exe to point to the Icarus Verilog root directory.\n");
448 fprintf(stderr," For a Windows command shell the option would be something like\n");
449 fprintf(stderr," -ivl=c:\\iverilog For a Cygwin shell the option would be something\n");
450 fprintf(stderr," like -ivl=c:\\\\iverilog\n");
451 myExit(6);
454 /* build up the CFLAGS option string */
456 assign(&gstr.pCFLAGS,IVERILOG_VPI_CFLAGS);
457 append(&gstr.pCFLAGS," -I");
458 append(&gstr.pCFLAGS,gstr.pIVL);
459 appendBackSlash(&gstr.pCFLAGS);
460 append(&gstr.pCFLAGS,"include");
462 /* build up the LDFLAGS option string */
464 assign(&gstr.pLDLIBS,"-L");
465 append(&gstr.pLDLIBS,gstr.pIVL);
466 appendBackSlash(&gstr.pLDLIBS);
467 append(&gstr.pLDLIBS,"lib ");
468 append(&gstr.pLDLIBS,IVERILOG_VPI_LDLIBS);
471 /* compile source modules */
473 static void compile(char *pSource, char **pObject, char *ext, int *compile_errors, char *compiler)
475 char *ptr1 = pSource;
476 char *ptr2 = strchr(pSource,' ');
477 char *buf=0,*src=0,*obj=0;
479 while (ptr2) {
480 int len = ptr2 - ptr1;
481 assignn(&src,ptr1,len);
483 assignn(&obj,ptr1,len-strlen(ext)); /* strip off the extension */
484 append (&obj,".o");
486 assign (&buf,compiler);
487 append (&buf," -c -o ");
488 append (&buf,obj);
489 append (&buf," ");
490 append (&buf,gstr.pCFLAGS);
491 append (&buf," ");
492 append (&buf,src);
494 append (pObject,obj);
495 append (pObject," ");
497 printf("%s\n",buf);
499 if (system(buf))
500 ++*compile_errors;
502 ptr1 = ptr2 + 1; /* advance to next token */
503 ptr2 = strchr(ptr1,' ');
506 free(buf);
507 free(src);
508 free(obj);
511 /* using the global strings, compile and link */
513 static void compile_and_link()
515 char *buf=0;
516 int iRet, compile_errors = 0;
518 /* print out the mingw and ivl directories to help the user debug problems */
520 printf("info: %s will be used as the MinGW root directory.\n",gstr.pMINGW);
521 checkMingwDir(gstr.pMINGW);
523 printf("info: %s will be used as the Icarus Verilog root directory.\n",gstr.pIVL);
524 checkIvlDir(gstr.pIVL);
526 /* compile */
528 compile(gstr.pCCSRC,&gstr.pOBJ,".c" ,&compile_errors,IVERILOG_VPI_CC ); /* compile the C source files */
529 compile(gstr.pCXSRC,&gstr.pOBJ,".cc",&compile_errors,IVERILOG_VPI_CXX); /* compile the C++ source files */
531 if (compile_errors) {
532 fprintf(stderr,"iverilog-vpi: Some %d files failed to compile.\n",compile_errors);
533 myExit(2);
536 /* link */
538 assign(&buf,IVERILOG_VPI_LD);
539 append(&buf," -o ");
540 append(&buf,gstr.pOUT); /* has a trailing space */
541 append(&buf,IVERILOG_VPI_LDFLAGS);
542 append(&buf," ");
543 append(&buf,gstr.pOBJ) /* has a trailing space */;
544 append(&buf,gstr.pLIB); /* has a trailing space */
545 append(&buf,gstr.pLDLIBS);
547 printf("%s\n",buf);
549 iRet = system(buf);
551 free(buf);
553 if (iRet)
554 myExit(3);
557 /* program execution starts here */
559 int main(int argc, char *argv[])
561 init();
563 if (!parse(argc,argv))
564 usage();
566 setup_mingw_environment();
567 setup_ivl_environment();
569 if (*gstr.pOUT) /* are there any *.c,*.cc,*.o files specified */
570 compile_and_link();
572 myExit(0);