Autor Wątek: Kompilacja i uruchamianie programu w programie  (Przeczytany 5845 razy)

Offline kpusmo

  • Użytkownik

# Lipiec 22, 2013, 13:59:57
Czy jest możliwość nakazania kompilacji podczas wykonywania programu?
Mam jeden program (nazwijmy go Nadrzedny), który na podstawie danych wejściowych tworzy plik Podrzedny.cpp. Jest możliwość, żeby Nadrzedny po utworzeniu Podrzedny.cpp uruchomil kompilator, a nastepnie wlaczyl Podrzedny?
Jaki jest dla linuxa odpowiednik windowsowego WinExec z biblioteki Windows.h?

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline shoter

  • Użytkownik

# Lipiec 22, 2013, 14:20:36
Nie pisz dwóch pytań w jeden temat, syf się robi.

Jakie środowisko?
Możliwe że w microsoft visual studio dałoby się coś takiego zrobić z wykorzystaniem komend po czasie kompilacji?

Offline kpusmo

  • Użytkownik

# Lipiec 22, 2013, 14:59:34
Przepraszam za dwa pytania, są ze sobą powiązane, więc myślałem, że mogę je tak zadać.
Pracuję na code::blocks na fedorze 19, kompilator g++4.8.1. Wolałbym, żeby ten sposób był w standardzie c++ (zależy mi na przeniesieniu później programu na windowsa).
Myślałem bardziej o wywołaniu najpierw czegoś typu WinExec() na kompilatorze, a następnie na skompilowanym programie. Nie wiem jak to by wyglądało w praktyce - kompilatorowi trzeba podać parametry, ścieżkę do pliku, no i trzeba znać ścieżkę samego kompilatora, a nie wiem czy istnieje coś takiego jak binarka kompilatora. W /bin znalazłem plik g++ ale nie mam pojęcia czy aby na pewno o to chodzi.

Offline Xion

  • Redaktor
    • xion.log

  • +1
# Lipiec 22, 2013, 15:14:55
Oczywiście że istnieje coś takiego jak binarka kompilatora i w /bin masz dużą szansę ją znaleźć :) Rzecz w tym, że o ile da się to zrobić na normalnych systemach, to pod Windows nie - kompilator nie jest tam częścią systemu, a jeśli nawet jest zainstalowany, to może być dosłownie wszędzie i być czymkolwiek (MSC, GCC, LLVM, etc.)

(Pod OSX kompilator też nie jest domyślnie zainstalowany, ale jeśli już jest, to najpewniej pochodzi z pakietu Command Line Tools - do ściągnięcia ze stron deweloperskich Apple'a - i jest to zwykłe GCC).

Jeśli chodzi o uruchaimanie procesów pod Linuksem etc., to fork() + execv() jest typową metodą.

Offline Veldrin

  • Użytkownik

# Lipiec 22, 2013, 15:36:03
Wystarczy, żeby proces A wywołał zdefiniowany skrypt budujący(jakiegoś make'a, sconsa) , a następnie go uruchomił przez nowy proces B.

A potem przez IPC mogą już ze sobą w pokera grać ;).

Offline kpusmo

  • Użytkownik

# Lipiec 22, 2013, 16:47:24
Xion ogromne dzięki za pomoc z uruchamianiem. Nie jestem tylko pewien jak używać fork(). Kiedy go używam razem z execv() to program uruchamia mi się podwójnie (wszystko wyświetla się i trzeba wpisywać dwa razy). Kiedy pominę fork() wszystko jest w porządku. Jak zrozumiałem z internetu fork() tworzy nowy proces i tak jakby oba te procesy (dotychczasowy i właśnie stworzony) były dominowane przez program uruchamiany przez execv(). Czytałem o funkcji fork(), ale nie znalazłem rozwiązania.
Spróbowałem też włączyć /bin/g++ i rzeczywiście, to odpowiedni plik - w konsoli pojawia mi się
Cytuj
Untitled1: fatal error: no input files
compilation terminated.
Kiedy przekazuję argumenty uruchamiając program w konsoli:
Cytuj
./Untitled1 prog.cpp
Wyświetla się:
Cytuj
Untitled1: error trying to exec 'cc1plus': execvp: Nie ma takiego pliku ani katalogu
Kompilator uruchamiam:
execv("/bin/g++", argv);Funkcja execv() jak już pisałem uruchamia mi dobrze inne programy, więc ona nie jest problemem.

EDIT:
W internecie przeczytałem o funkcji system(const char*) i zamiast
execv("/bin/g++", argv);dałem:
system("g++ prog.cpp -o program");Dostaję komunikaty:
Cytuj
/usr/lib/gcc/x86_64-redhat-linux/4.8.1/../../../../lib64/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status
Oto cały program:
#include <iostream>
#include <fstream>
#include <unistd.h>
#include <cstdlib>
using namespace std;
int main(int argc, char *argv[])
{

    ofstream out("prog.cpp");
    out <<  "#include <iostream>\n"
            "using namespace std;\n"
            "int main()\n"
            "{\n"
            "cout << \"Hello world\";\n"
            "return 0;\n"
            "}";


    //execv("/bin/g++", argv);
    system("g++ prog.cpp -o program");
    return 0;
}
O co tu w końcu chodzi? Nie widzi jakiegoś pliku, ma problem z funkcją main? prog.cpp z poziomu terminala jak i przez code:blocks kompiluje się bez problemu, a skompilowany plik bez zarzutu uruchamia się z poziomu programu nadrzędnego (przez execv();).
« Ostatnia zmiana: Lipiec 22, 2013, 17:48:08 wysłana przez kpusmo »

Offline Avaj

  • Użytkownik

  • +1
# Lipiec 22, 2013, 18:37:14
daj dla pewniaka flush strumienia (albo wrzuć std::endl na końcu), żeby mieć pewność że plik nie jest pusty podczas system()

Offline kpusmo

  • Użytkownik

# Lipiec 22, 2013, 18:47:56
Kurcze, działa!:D O głupiego endla chodziło:D Ogromne dzięki Avaj i wszyscy, którzy pomagali:) A wie ktoś może czemu execv("/bin/g++", argv) wciąż nie działa? Wydaje mi się, że jest to szybsza metoda niż wywoływanie system(), który podobno robi niezły bałagan...
Dla execv wciąż wyświetla się:
Cytuj
Untitled1: error trying to exec 'cc1plus': execvp: Nie ma takiego pliku ani katalogu

Offline Xirdus

  • Moderator

# Lipiec 22, 2013, 21:30:28
Pierwsze pole argv musi (w sumie tylko powinno, a tak w ogóle to tylko tak się przyjęło) odpowiadać nazwie wołanego programu (czyli to co robisz w pierwszym parametrze). Pierwszy parametr jest w argv[1], i tak dalej.

Nie możesz po prostu przekazać argv, którego dostałeś do maina. Musisz go nieco zmodyfikować.

Offline Dab

  • Redaktor
    • blog

  • +2
# Lipiec 22, 2013, 21:47:56
Jak robisz to pod Unixa to jest tam fajna funkcja popen. Przykład:

#include <stdio.h>
int main() {
FILE * q;
int n;

q = popen("g++ -x c++ -o popentest -", "w");
fprintf(q, "#include <stdio.h>\nint main() { printf(\"%%d\", fgetc(stdin)); }");
pclose(q);
q = popen("./popentest", "r+");
fprintf(q, "a");
fflush(q);
fscanf(q, "%d", &n);
pclose(q);
printf("Result = %d (%d?)\n", n, 'a');
}

Offline Xirdus

  • Moderator

# Lipiec 22, 2013, 21:52:40
Warto wspomnieć, że Windowsowym odpowiednikiem popen() jest funkcja _popen() :)

Offline Dab

  • Redaktor
    • blog

# Lipiec 22, 2013, 21:57:56
Errata: w powyższym się rozpędziłem i użyłem dostępnego w OSX/BSD dwukierunkowego popen. Normalny, Linuksowy popen działa tylko w jedną stronę, Windowsowa emulacja którą podlinkował Xirdus też.

Offline bies

  • Użytkownik

  • +1
# Lipiec 22, 2013, 22:04:33
http://libexecstream.sourceforge.net/ -- działa jak dwukierunkowy popen na Linuksie i Windows.

Offline Xirdus

  • Moderator

# Lipiec 22, 2013, 22:04:56
Nie doczytałem do końca MSDN i nie zauważyłem tej bardzo ważnej notki:
Cytuj
Note   If used in a Windows program, the _popen function returns an invalid file pointer that will cause the program to hang indefinitely. _popen works properly in a Console application. To create a Windows application that redirects input and output, see Creating a Child Process with Redirected Input and Output in the Platform SDK.

Offline kpusmo

  • Użytkownik

# Lipiec 23, 2013, 19:20:14
Dzięki za podrzucenie popen:) wykonanie programu dla system();
Cytuj
real   0m0.344s
user   0m0.281s
sys     0m0.059s
dla popen():
Cytuj
real   0m0.007s
user   0m0.004s
sys     0m0.002s
tak więc ten...;) argv zmieniałem różnie, ale przy żadnej kombinacji celu nie osiągnąłem. Będę po prostu używał popen.
Tak btw, czym jest ta "kierunkowość" funkcji? Chodzi o obsługiwanie wejścia i wyjścia (dwukierunkowa) bądź jednego z nich (jednokierunkowa)?
@bies dzięki za link, ale wyznaję zasadę, że im mniej dodatkowych bibliotek zainstalowanych tym lepiej:D


EDIT Udało mi się zmienić argv - wystarczyło za argv[0] podać "/bin/g++" (próbowałem to robić kopiując argv do innej tablicy, żeby nie ruszać jego samego, ale nie pomogło - dopiero ingerencja w argument pomogła;]). Ale tutaj przychodzi kolejny problem: jeśli najpierw kompiluję przez execv("/bin/g++", argv); a później próbuję włączyć skompilowany program - nic się nie dzieje. Dopiero kiedy usunę execv włączające kompilator program się włącza.
popen pomógł, ale również jest problem. Używam go do kompilacji, uruchamiam execv:
    popen("g++ prog.cpp -o prog", "r");
    execv("prog", argv);
Problem w tym, że prog.cpp jest tworzone dopiero w programie:
    ofstream out("prog.cpp");
Kiedy prog.cpp na dysku nie istnieje, jest wywoływana tylko kompilacja - program się nie uruchamia. Dopiero kiedy prog.cpp jest stworzony (przy następnym uruchomieniu Untitled1) prog się włącza. Nie trzeba usuwać linii kompilującej program.
Kompilacja przez system() działa dobrze, ale jest za wolna:)

    //char* arg = argv[0];
    //argv[0] = "/bin/g++";
    //execv("/bin/g++", argv); //kompiluje, ale trzeba tę linię usunąć
                                            //żeby uruchomić program
    //system("g++ prog.cpp -o prog"); //działa dobrze, ale jest wolne
    //popen("g++ prog.cpp -o prog", "r"); //działa dobrze, jeśli plik jest na dysku w czasie kompilacji
   
    //argv[0] = "prog"; // bądź argv[0] = arg; - oba działają tak samo
   
    execv("prog", argv);

Jak uruchomić program przez popen, tak żeby się wykonał? W przykładzie, który podał Dab jest tylko łączenie pliku ze wskaźnikiem, poza tym przykład narusza ochronę pamięci (domyślam się, że to skutek jednokierunkowości funkcji).
« Ostatnia zmiana: Lipiec 23, 2013, 21:01:40 wysłana przez kpusmo »