Hi folks,
Here is the first cut at winewrap, as described here: http://www.dssd.ca/wine/Winelib-Apps.html
What it does at the moment: -- invokes winebuild to create the .spec.c file -- compiles the .spec.s file -- links in everything, and generates the .exe.so file
What remains to be done: -- generate the actual executable.
One possible option is to modify wineapploader so we simply symlink to it. I have a version of this around. Is this acceptable?
Comments welcomes.
/* * Wine wrapper: takes care of internal details for linking. * * Copyright 2002 Dimitrie O. Paun * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "config.h" #include "wine/port.h"
#include <stdio.h> #include <string.h> #include <stdarg.h> #include <stdlib.h> #include <sys/param.h> #include <sys/wait.h> #ifdef HAVE_UNISTD_H # include <unistd.h> #endif
#ifndef WINEDLLS #define WINEDLLS "/usr/local/lib/wine" #endif
static char *output_name; static char **lib_files, **res_files, **obj_files; static int nb_lib_files, nb_res_files, nb_obj_files;
void error(const char *s, ...) { va_list ap;
va_start(ap, s); fprintf(stderr, "Error: "); vfprintf(stderr, s, ap); fprintf(stderr, "\n"); va_end(ap); exit(2); }
char *strmake(const char *fmt, ...) { int n, size = 100; char *p; va_list ap;
if ((p = malloc (size)) == NULL) return NULL;
while (1) { va_start(ap, fmt); n = vsnprintf (p, size, fmt, ap); va_end(ap); if (n > -1 && n < size) return p; if (n > -1) size *= 2; if ((p = realloc (p, size)) == NULL) return NULL; } }
void spawn(char *const argv[]) { int pid, status, i;
for(i = 0; argv[i]; i++) printf("%s ", argv[i]); printf("\n");
if ((pid = fork()) == 0) execvp(argv[0], argv); else if (wait(&status) > 0) { if (WEXITSTATUS(status) == 0) return; else error("%s failed.", argv[0]); } perror(0); exit(1); }
int main(int argc, char **argv) { char *library = 0; int i, j, len, is_object, no_opt = 0; char cmd[PATH_MAX]; char **spec_args, **comp_args, **link_args;
for (i = 1; i < argc; i++) { if (!no_opt && argv[i][0] == '-') { switch (argv[i][1]) { case 'o': if (argv[i][2]) output_name = strdup(argv[i]+ 2); else if (i + 1 < argc) output_name = strdup(argv[++i]); else error("The -o switch takes an argument\n."); len = strlen(output_name); if (len > 4 && strcmp(output_name + len - 4, ".exe") == 0) output_name[len - 4] = 0; break; case 'l': if (argv[i][2]) library = argv[i]+ 2; else if (i + 1 < argc) library = argv[++i]; else error("The -l switch takes an argument\n."); if (strcmp(library, "winspool") == 0) library = "winspool.drv"; lib_files = realloc( lib_files, (nb_lib_files+1) * sizeof(*lib_files) ); lib_files[nb_lib_files++] = strdup(library); break; case '-': if (argv[i][2]) error("No long option supported\n"); no_opt = 1; break; default: error("Unknown option -%c\n", argv[i][1]); } continue; } /* it's a filename, not an option: test if it is a resource */ snprintf(cmd, sizeof(cmd), "readelf -V %s >/dev/null 2>/dev/null", argv[i]); is_object = (system(cmd) == 0);
/* add it to its respective list */ if (is_object) { obj_files = realloc( obj_files, (nb_obj_files+1) * sizeof(*obj_files) ); obj_files[nb_obj_files++] = strdup(argv[i]); } else { res_files = realloc( res_files, (nb_res_files+1) * sizeof(*res_files) ); res_files[nb_res_files++] = strdup(argv[i]); } }
/* build winebuild's argument list */ spec_args = malloc( (nb_lib_files + nb_obj_files + nb_res_files * 2 + 20) * sizeof (char *) ); j = 0; spec_args[j++] = "winebuild"; spec_args[j++] = "-fPIC"; spec_args[j++] = "-o"; spec_args[j++] = strmake("%s.spec.c", output_name); spec_args[j++] = "--exe"; spec_args[j++] = strmake("%s.exe", output_name); spec_args[j++] = "-m"; spec_args[j++] = "gui"; for (i = 0; i < nb_res_files; i++) { spec_args[j++] = "-r"; spec_args[j++] = res_files[i]; } for (i = 0; i < nb_obj_files; i++) spec_args[j++] = obj_files[i]; spec_args[j++] = "-L" WINEDLLS; for (i = 0; i < nb_lib_files; i++) spec_args[j++] = strmake("-l%s", lib_files[i]); spec_args[j++] = "-lmsvcrt"; spec_args[j] = 0;
/* build gcc's argument list */ comp_args = malloc (20 * sizeof (char *) ); j = 0; comp_args[j++] = "gcc"; comp_args[j++] = "-fPIC"; comp_args[j++] = "-c"; comp_args[j++] = strmake("%s.spec.c", output_name);
/* build ld's argument list */ link_args = malloc( (nb_obj_files + 20) * sizeof (char *) ); j = 0; link_args[j++] = "gcc"; link_args[j++] = "-shared"; link_args[j++] = "-Wl,-Bsymbolic"; link_args[j++] = "-lwine"; link_args[j++] = "-lm"; link_args[j++] = "-o"; link_args[j++] = strmake("%s.exe.so", output_name); link_args[j++] = strmake("%s.spec.o", output_name); for (i = 0; i < nb_obj_files; i++) link_args[j++] = obj_files[i]; link_args[j] = 0;
/* run winebuild to get the .spec.c file */ spawn(spec_args);
/* compile the .spec.c file */ spawn(comp_args);
/* run gcc to link */ spawn(link_args);
return 0; }
"Dimitrie O. Paun" dpaun@rogers.com writes:
One possible option is to modify wineapploader so we simply symlink to it. I have a version of this around. Is this acceptable?
I don't think a symlink would work. IMO it should be a self-contained thing that you can move around, copy to other machines, etc.
On December 6, 2002 12:16 pm, Alexandre Julliard wrote:
I don't think a symlink would work. IMO it should be a self-contained thing that you can move around, copy to other machines, etc.
Well, depends on what you call "work" :))) I've attached what I had in mind for a generic wineapploader script.
But regardless, you want to simply output the script, or generate an ELF executable?
Also, should I delete the intermediate files? I think I should, one can get to them by manually running winebuild.
"Dimitrie O. Paun" dpaun@rogers.com writes:
Well, depends on what you call "work" :))) I've attached what I had in mind for a generic wineapploader script.
The problem I see with the symlink is that it's too fragile. As soon as you move/rename anything it breaks. Copying the script is already better, but I also think we should hardcode the app name instead of relying on $0, so that you can rename the script and it still works.
Also, should I delete the intermediate files? I think I should, one can get to them by manually running winebuild.
Yes, and you should use temp file names I think.
On December 6, 2002 12:51 pm, Alexandre Julliard wrote:
The problem I see with the symlink is that it's too fragile. As soon as you move/rename anything it breaks. Copying the script is already better, but I also think we should hardcode the app name instead of relying on $0, so that you can rename the script and it still works.
Fair enough. But I would suggest we modify the script (as in the one I sent) to allow the execution of the app out of _its_ build tree. There isn't much point in requiring the app to be in the path, no? Moreover, many Win32 apps don't even have an install target to their Makefiles, so running out of the build tree is the only option.
Also, should I delete the intermediate files? I think I should, one can get to them by manually running winebuild.
Yes, and you should use temp file names I think.
Fine. What about the readelf hack? Should I open the file myself, and check for the ELF signature. As far as I can say, the .res files have no signature, right?
"Dimitrie O. Paun" dpaun@rogers.com writes:
Fair enough. But I would suggest we modify the script (as in the one I sent) to allow the execution of the app out of _its_ build tree. There isn't much point in requiring the app to be in the path, no?
Sure, it should run from the build directory (though we should fall back to using the path if we don't find it there). Isn't that what the script already does?
Fine. What about the readelf hack? Should I open the file myself, and check for the ELF signature. As far as I can say, the .res files have no signature, right?
The 32-bit ones do, so it would be possible to identify them. But actually I could hack winebuild to do that itself, and then simply get rid of the -r option.
On December 6, 2002 01:19 pm, Alexandre Julliard wrote:
Sure, it should run from the build directory (though we should fall back to using the path if we don't find it there). Isn't that what the script already does?
I don't remember now, there was something I didn't like. :) Nevermind that, now the question is: should I include the script in winewrap? Otherwise, we have to install wineapploader, and I don't quite see the point in that. We can have a --loader option that allows you to use an external script if need be, in which case we can maybe even simplify the generated script a bit.
The 32-bit ones do, so it would be possible to identify them. But actually I could hack winebuild to do that itself, and then simply get rid of the -r option.
I think this is a better option, and the reason is that it would allow people to more easily use winebuild directly, which is preferable because the generated files will be in sync with the Makefile targets (whereas with winewrap, we're generating files the Makefile doesn't know about).
"Dimitrie O. Paun" dpaun@rogers.com writes:
Nevermind that, now the question is: should I include the script in winewrap? Otherwise, we have to install wineapploader, and I don't quite see the point in that. We can have a --loader option that allows you to use an external script if need be, in which case we can maybe even simplify the generated script a bit.
I think winewrap should be self-contained and not depend on an external script. The fact that we generate a script is just an implementation detail, at some point it may become an ELF binary so we don't want anybody to depend on that.
On December 6, 2002 01:19 pm, Alexandre Julliard wrote:
The 32-bit ones do, so it would be possible to identify them. But actually I could hack winebuild to do that itself, and then simply get rid of the -r option.
Well, in this case, why not merge winewrap into winebuild, and maybe provide flags to do only certain steps, sort of like gcc?
In fact, winebuild already follows this philosophy, so it seems only natural. And we can avoid yet another small wrapper.
"Dimitrie O. Paun" dpaun@rogers.com writes:
Well, in this case, why not merge winewrap into winebuild, and maybe provide flags to do only certain steps, sort of like gcc?
In fact, winebuild already follows this philosophy, so it seems only natural. And we can avoid yet another small wrapper.
That would be a possibility, but you'd need some restructuring of winebuild to do that (or have it exec itself which doesn't seem very clean). I don't know, it feels cleaner to have a separate wrapper; I'd suggest doing at least the first version that way, and once things are cleared up maybe we can find a way to merge everything into winebuild cleanly.
On December 6, 2002 03:12 pm, Alexandre Julliard wrote:
I don't know, it feels cleaner to have a separate wrapper; I'd suggest doing at least the first version that way, and once things are cleared up maybe we can find a way to merge everything into winebuild cleanly.
Yes, I agree it feels cleaner to keep it as a wrapper, but current practice points in the other direction. Nevertheless, I fully agree that it makes sense to have the first cut done separately, so we better understand what the problem is before we do anything else.
That being said, if we do plan to to integrate it into winebuild, I think we should do it sooner, rather than later, even at the expense of small temporary ugliness (e.g. fork winebuild from itself) in winebuild. We are in Wine 0.9 mode right now, and we should try hard to stabilize the utilities, and the *process*. The importance of said stabilization can not be overstated, as a bunch of other things depend on it, such as documentation, and other scripts (such as winemaker). And these dependents have been historically a _lot_ slower to change than C code. But most importantly, if people start using Winelib, it's going to be very hard to change established process as everyone's build system depends on it.
Anyway, I will try to finish winewrap in it's current form, and we can evaluate merging it into winebuild when I'm done.
On December 6, 2002 03:12 pm, Alexandre Julliard wrote:
That would be a possibility, but you'd need some restructuring of winebuild to do that (or have it exec itself which doesn't seem very clean). I don't know, it feels cleaner to have a separate wrapper;
Unfortunately, this doesn't work as nicely as a separate script, as I have to segragate the object files from resource files again, to pass only proper object files to gcc for linking.
In the current script I have added a hack (assume files with .res in the name are resource files) to get around this problem, and be able to test the script out.
I have tested this script with putty, and it works beautifully. At this point I still think it's better we integrate it into winebuild. Appart from the .res/.o detection, it avoids having to duplicate -m gui|cui handling, among other things.
Comments welcomed, as usual.
/* * Wine wrapper: takes care of internal details for linking. * * Copyright 2002 Dimitrie O. Paun * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "config.h" #include "wine/port.h"
#include <stdio.h> #include <string.h> #include <stdarg.h> #include <stdlib.h> #include <sys/wait.h> #include <sys/stat.h> #ifdef HAVE_UNISTD_H # include <unistd.h> #endif
#ifndef WINEDLLS #define WINEDLLS "/usr/local/lib/wine" #endif
static const char *app_loader_script = "#!/bin/sh\n" "\n" "appname="%s"\n" "# determine the application directory\n" "appdir=''\n" "case "$0" in\n" " */*)\n" " # $0 contains a path, use it\n" " appdir=`dirname "$0"`\n" " ;;\n" " *)\n" " # no directory in $0, search in PATH\n" " saved_ifs=$IFS\n" " IFS=:\n" " for d in $PATH\n" " do\n" " IFS=$saved_ifs\n" " if [ -x "$d/$appname" ]; then appdir="$d"; break; fi\n" " done\n" " ;;\n" "esac\n" "\n" "# figure out the full app path\n" "if [ -n "$appdir" ]; then\n" " apppath="$appdir/$appname.exe.so"\n" "else\n" " apppath="$appname.exe.so"\n" "fi\n" "\n" "# determine the WINELOADER\n" "if ! [ -x "$WINELOADER" ]; then WINELOADER="wine"; fi\n" "\n" "# and try to start the app\n" "exec "$WINELOADER" "$apppath" "$@"\n" ; static char *output_name; static char **lib_files, **obj_files; static int nb_lib_files, nb_obj_files; static int debug = 1;
void error(const char *s, ...) { va_list ap;
va_start(ap, s); fprintf(stderr, "Error: "); vfprintf(stderr, s, ap); fprintf(stderr, "\n"); va_end(ap); exit(2); }
char *strmake(const char *fmt, ...) { int n, size = 100; char *p; va_list ap;
if ((p = malloc (size)) == NULL) return NULL;
while (1) { va_start(ap, fmt); n = vsnprintf (p, size, fmt, ap); va_end(ap); if (n > -1 && n < size) return p; if (n > -1) size *= 2; if ((p = realloc (p, size)) == NULL) return NULL; } }
void spawn(char *const argv[]) { int pid, status, i;
if (debug) { for(i = 0; argv[i]; i++) printf("%s ", argv[i]); printf("\n"); }
if ((pid = fork()) == 0) execvp(argv[0], argv); else if (wait(&status) > 0) { if (WEXITSTATUS(status) == 0) return; else error("%s failed.", argv[0]); } perror(0); exit(1); }
int main(int argc, char **argv) { char *library = 0, *p; int i, j, no_opt = 0; char *spec_name, *spec_c_name, *spec_o_name; char **spec_args, **comp_args, **link_args; FILE *file;
for (i = 1; i < argc; i++) { if (!no_opt && argv[i][0] == '-') { switch (argv[i][1]) { case 'o': if (argv[i][2]) output_name = strdup(argv[i]+ 2); else if (i + 1 < argc) output_name = strdup(argv[++i]); else error("The -o switch takes an argument."); if ( !(p = strstr(output_name, ".exe")) || *(p+4) ) output_name = strmake("%s.exe", output_name); break; case 'l': if (argv[i][2]) library = argv[i]+ 2; else if (i + 1 < argc) library = argv[++i]; else error("The -l switch takes an argument\n."); if (strcmp(library, "winspool") == 0) library = "winspool.drv"; lib_files = realloc( lib_files, (nb_lib_files+1) * sizeof(*lib_files) ); lib_files[nb_lib_files++] = strdup(library); break; case '-': if (argv[i][2]) error("No long option supported."); no_opt = 1; break; default: error("Unknown option -%c\n", argv[i][1]); } continue; } /* it's a filename, add it to its list */ obj_files = realloc( obj_files, (nb_obj_files+1) * sizeof(*obj_files) ); obj_files[nb_obj_files++] = strdup(argv[i]); }
spec_name = tempnam(0, 0); spec_c_name = strmake("%s.c", spec_name); spec_o_name = strmake("%s.o", spec_name);
/* build winebuild's argument list */ spec_args = malloc( (nb_lib_files + nb_obj_files + 20) * sizeof (char *) ); j = 0; spec_args[j++] = "winebuild"; spec_args[j++] = "-fPIC"; spec_args[j++] = "-o"; spec_args[j++] = strmake("%s.c", spec_name); spec_args[j++] = "--exe"; spec_args[j++] = output_name; spec_args[j++] = "-m"; spec_args[j++] = "gui"; for (i = 0; i < nb_obj_files; i++) spec_args[j++] = obj_files[i]; spec_args[j++] = "-L" WINEDLLS; for (i = 0; i < nb_lib_files; i++) spec_args[j++] = strmake("-l%s", lib_files[i]); spec_args[j++] = "-lmsvcrt"; spec_args[j] = 0;
/* build gcc's argument list */ comp_args = malloc (20 * sizeof (char *) ); j = 0; comp_args[j++] = "gcc"; comp_args[j++] = "-fPIC"; comp_args[j++] = "-o"; comp_args[j++] = spec_o_name; comp_args[j++] = "-c"; comp_args[j++] = spec_c_name;
/* build ld's argument list */ link_args = malloc( (nb_obj_files + 20) * sizeof (char *) ); j = 0; link_args[j++] = "gcc"; link_args[j++] = "-shared"; link_args[j++] = "-Wl,-Bsymbolic"; link_args[j++] = "-lwine"; link_args[j++] = "-lm"; link_args[j++] = "-o"; link_args[j++] = strmake("%s.so", output_name); link_args[j++] = spec_o_name; for (i = 0; i < nb_obj_files; i++) if (!strstr(obj_files[i], ".res")) link_args[j++] = obj_files[i]; link_args[j] = 0;
/* run winebuild to get the .spec.c file */ spawn(spec_args);
/* compile the .spec.c file */ spawn(comp_args); unlink(spec_c_name);
/* run gcc to link */ spawn(link_args); unlink(spec_o_name);
/* create the loader script */ output_name[strlen(output_name) - 4] = 0; /* remove the .exe extension */ if ( !(file = fopen(output_name, "w")) ) error ("Can not create %s.", output_name); fprintf(file, app_loader_script, output_name); fclose(file); chmod(output_name, S_IXUSR | S_IXGRP | S_IXOTH);
return 0; }