Forum Programmation.autre détecter l'OS depuis un code compilé

Posté par  . Licence CC By‑SA.
Étiquettes :
2
10
fév.
2018

Bonjour,

Je développe sous Linux un code de calcul en Fortran 2008. Ce code tourne aussi sous MS-Windows. Pour compiler les versions MS-Windows (32 ou 64 bits) je fais de la compilation croisée sous Linux.
Les problèmes de portabilité entre Linux et MS-Windows se limitent essentiellement à la gestion des chemins de fichier à cause du séparateur de répertoire qui n'est pas le même sur les deux OS. J'ai donc une variable OS définie à la compilation qui permet au code de savoir pour quel OS il est compilé.

Mon problème maintenant est que je veux tester la version MS-Windows sous Linux avec Wine afin de pouvoir aisément lancer les mêmes tests automatiques que pour l'exécutable Linux sans m'embêter à passer sur une VM MS-Windows.
Le problème est donc que maintenant le code a besoin de savoir sur quel OS il tourne, indépendamment de l'OS pour lequel il a été compilé.

À ma connaissance il n'existe pas de procédure intrinsèque Fortran qui fournisse des informations sur l'OS. En revanche comme la procédure intrinsèque execute_command_line() permet d'exécuter n'importe quelle commande shell, je peux m'affranchir des limites du langage pour détecter l'OS. Par exemple exécuter un script Python qui marcherait aussi bien sous Linux que sous MS-Windows, sauf que Python n'est pas installé par défaut sur MS-Windows. Ou bien tester une ou plusieurs commandes shell et en déduire l'OS selon ce qu'elles renvoient ou échouent à renvoyer. Ou peut-être qu'on peut obtenir l'information avec un bout de code C ?.

Est-ce que vous auriez un moyen de faire cela à me suggérer ?

  • # #ifdef

    Posté par  (site web personnel) . Évalué à 2.

    Je m'en étais sorti il y a quelques années entre deux compilateurs (mais sous le même OS) en mettant dans une toute partie du code les tests qui allait bien via le précompilateur afin de les cloisonner…

    #ifdef __INTEL_COMPILER
    ...
    #endif
    
    #if defined (__GNUC__) || defined (XLF)

    Je pense qu'en regardant bien les variables des compilateurs, on doit pouvoir faire de même.

    • [^] # Re: #ifdef

      Posté par  . Évalué à 8.

      Si j'ai bien compris, ce n'est pas ce qu'il veut. Ce qu'il veut c'est que du code compilé pour windows s'execute differement suivant si il est sur l'archi de test ou sur une machine d'un utilisateur.

      Ça me rappelle quelque chose, qu'un programme detecte à l'execution si il est ou non sur l'archi de test pour pouvoir prendre des décisions différentes… genre Volkswagen.

      • [^] # Re: #ifdef

        Posté par  . Évalué à 1. Dernière modification le 10 février 2018 à 14:07.

        Oui à peu près, mais là c'est juste pouvoir décider si slash = '/' ou slash = '\' de façon que le décodage des chemins fonctionne dans tous les cas. ;-)

        À la réflexion, ce dont j'ai besoin pour l'instant c'est en fait détecter si le code est en train de tourner sur une machine Linux. Donc un script basé sur la commande uname devrait me donner la réponse, il faut juste que je traite correctement l'échec du script dans le cas où je le fais tourner sur MS-Windows.
        S'il y avait une variable d'environnement qui donne l'info ce serait quand même plus pratique.

        • [^] # Re: #ifdef

          Posté par  (site web personnel) . Évalué à 6.

          Trouver le bon séparateur de chemins en Fortran est sûrement une question fréquemment posée.
          Cf https://stackoverflow.com/questions/8624860/get-file-separator-in-fortran#8625905

          • [^] # Re: #ifdef

            Posté par  . Évalué à 2.

            Ah, merci. Je n'ai pas le réflexe stackoverflow, je devrais. Et puis j'aurais du refaire ma recherche en restreignant la question à celle du séparateur de fichier. Je vais faire des essais lundi avec une fonction inspirée de ce bout de code. Je suis curieux de voir quelle valeur de PATH je vais obtenir à travers Wine.

            Cela dit la question n'est pas si souvent posée. Celle qui l'est c'est celle à laquelle a répondu Sytoka et ça je fais déjà.
            Une autre présentation de mon problème : comment mon exécutable win32 ou win64 peut-il détecter qu'il tourne en réalité sur une machine Linux avec Wine ?

            Une autre piste : Fortran 2008 permet de tester l'existence d'un fichier ou d'un dossier, je peux tester l'existence de /bin ou /boot pour savoir si l'OS est Linux, à moins qu'il y ait un problème de droits pour obtenir une réponse. Je testerai.

            • [^] # Re: #ifdef

              Posté par  (site web personnel) . Évalué à 7.

              comment mon exécutable win32 ou win64 peut-il détecter qu'il tourne en réalité sur une machine Linux avec Wine ?

              Normalement il ne devrait pas avoir besoin de le savoir. Souvent ceux qui ont besoin de savoir s'ils sont dans un environnement virtualisé, simulé, émulé, chrooté, jailbreaké, ptracé, bref observé/contrôlé/bridé, sont les programmes malveillants (virus/ver/etc., programme de fraude pour moteur de voiture ou pour caisse enregistreuse ou machine à sous dissimulée, etc.). Savoir si tu es sous un Unix (avec son '/' et sa fin de ligne 'LF') ou un Windows (avec son '\' et sa fin de ligne 'CR LF') c'est normal, maintenant avec Wine, tu ne devrais pas avoir besoin de le savoir (lancé Wine sur un Unix ou un MacOS à l'ancienne avec sa fin de ligne 'CR' (je ne crois pas que ça soit possible mais bon c'est pour l'exemple) ça ne devrait pas être le souci du programme, mais de Wine lui-même).

  • # Mauvaise idée ?

    Posté par  (site web personnel) . Évalué à 4.

    Détecter dynamiquement :

    • la version de Windows (seven, 8 ou 10), afin d'utiliser des fonctionnalités qui existent sur une version récente tout en fonctionnant sur une version plus ancienne.
    • le nombre de CPU, afin de créer un nombre intelligent de threads.
    • la présence de certaines instructions (SSE et cie), afin d'utiliser une version plus optimisée du code quand c'est possible.

    tout en ayant un seul binaire estampillé "Windows", ça me parait une bonne idée pour éviter d'avoir 1000 combinaisons (genre seven+2cores+intel, w10+8cores+amd) qui vont être un enfer pour l'utilisateur.

    En revanche détecter qu'on est sur Wine ça ne va conduire qu'à un seule chose à mon avis: un programme qui tourne bien sous Wine, mais pas sur un vrai Windows (puisque tout un pan du code ne sera jamais testé).

    • [^] # Re: Mauvaise idée ?

      Posté par  . Évalué à 1.

      À part le nombre de cpu disponibles qui s'obtient à l'exécution avec OpenMP, les autres détections me semblent devoir être faites à la compilation. Et comme la production des exécutables Windows est faite sous Linux, c'est un exécutable générique que je produis. Je n'ai pas trouvé de réglage plus efficace que l'option -O3 de gcc/gfortran.

      • [^] # Re: Mauvaise idée ?

        Posté par  (site web personnel) . Évalué à 1.

        Désolé si je n'ai pas été clair ; je ne dis pas que ces détections dynamiques sont intéressantes dans votre cas précis (que je ne connais pas), c'était juste des exemples où la détection à l'exécution avait du sens. Le cœur de mon propos étant que ce que voulez faire ne fait pas partie de cette liste de cas pertinents, pour la bonne raison que c'est sûrement une mauvaise idée. Parce que vous voulez tester que ça fonctionne sous Windows (c'est très bien) mais vous sabordez le travail juste derrière (et ça c'est moins bien).

        Je n'ai pas trouvé de réglage plus efficace que l'option -O3 de gcc/gfortran.

        Je ne suis pas sûr de comprendre le lien avec mon message ; si vous répondez à ma partie sur la détection du CPU, voici une explication plus détaillée.

        Si vous détectez à l'exécution le type de CPU, vous pouvez par exemple utiliser des versions spécifiques écrites en langage assembleur avec des instructions spécifiques à l'architecture. Le noyau Linux a par exemple des versions écrites en langage assembleur pour ses algorithmes de chiffrement, qui sont utilisées dynamiquement quand c'est possible. Mais ça nécessite du travail de la part du codeur (ce n'est pas juste un flag compilateur), aussi le jeu doit en valoir la chandelle. Et même si vous compiliez pour cette architecture spécifique avec gcc, il n'est pas dit que le compilateur soit assez intelligent pour générer de lui-même la version la plus optimisée (sinon les développeurs Linux auraient généré ce code plutôt que de l'écrire à la main…).

  • # Résultats de mes tests

    Posté par  . Évalué à 2.

    J'ai fait quelques essais ce matin :
    - exécuter une commande genre uname : ça ne marche pas car c'est Wine qui répond et il ne connaît pas les commandes Linux ;
    - analyser la variable d'environnement PATH : encore raté, si le code tourne sous Wine, c'est le PATH Windows qui est renvoyé ;
    - tester l'existence du fichier /bin/. : ça ça marche

    Au final, cependant, changer le séparateur de fichier seulement sur la base de l'OS cible et de l'OS hôte, complique les choses. Je peux tester avec Wine un code Windows sur des données Linux, mais je ne peux plus tester avec Wine un code Windows sur des données Windows. Donc ça ouvre une porte et ça en ferme une autre.
    Du coup, je pense que le plus raisonnable est de laisser la main à l'utilisateur sur cette question, surtout que l'utilisateur en question c'est moi, les autres utilisateurs ne se poseront pas cette question. J'ai donc implémenté la lecture d'une variable d'environnement ad-hoc qui me permet de changer le séparateur de fichier seulement quand je le décide explicitement. Une option de la ligne de commande serait plus compliquée à implémenter car il y a une autre option avec un nom de fichier, donc potentiellement un séparateur de fichier, il me faut donc fixer le séparateur de fichier avant de décoder la ligne de commande.

    En tous les cas un grand merci à tous pour vos commentaires.

    • [^] # Re: Résultats de mes tests

      Posté par  . Évalué à 2.

      en meme temps si tu executes ton code sous WINE, c'est pour faire croire à ce code que la machine est sous Windows

      donc tes deux premiers resultats ne me choquent pas.

      et ca ne correspond pas à l'enoncé premier de detecter si tu es sous windows natif ou sous linux natif
      en effet l'appel system lors de l'execution devrait permettre de detecter l'OS
      mais ce n'est alors pas à la compilation que tu detectes l'OS sur lequel tu compiles, mais bien à l'execution du programe

      avec un simple ls ou un dir
      ca devrait deja te dire si la commande existe ou pas,
      et si elle n'existe pas, c'est que tu n'es pas sur l'OS visé (ls = linux, dir = windows)

      • [^] # Re: Résultats de mes tests

        Posté par  . Évalué à 1.

        et ca ne correspond pas à l'enoncé premier de detecter si tu es sous windows natif ou sous linux natif
        en effet l'appel system lors de l'execution devrait permettre de detecter l'OS
        mais ce n'est alors pas à la compilation que tu detectes l'OS sur lequel tu compiles, mais bien à l'execution
        du programe

        Mon problème n'est pas la compilation, je compile toujours sous Linux, même pour produire un exécutable win32 ou win64. Là il n'y a pas de détection d'OS, juste un switch dans le makefile.

        avec un simple ls ou un dir
        ca devrait deja te dire si la commande existe ou pas,
        et si elle n'existe pas, c'est que tu n'es pas sur l'OS visé (ls = linux, dir = windows)

        Non, justement c'est le même cas que celui de mon 1er résultat : sous Linux, un programme win64 exécuté avec Wine me répondra que la commande ls n'existe pas et que la commande dir existe, donc ça ne me permet pas de savoir que l'OS sous-jacent est Linux. En plus il faut analyser la réponse pour détecter l'erreur, c'est plus compliqué que juste récupérer un booléen me disant si le fichier /bin/. existe ou pas. Le bout de code Fortran pour faire cela pourrait être quelque chose comme ça :

        detection_Wine: block
        logical :: bExist
        inquire(file='/bin/.',exist=bExist)
        if (bExist) slash='/'
        end block detection_Wine

        avec slash une variable définie précédemment en fonction de la variable OS définie à la compilation pour l'OS cible et insérée dans le code grâce à un include.

        • [^] # Re: Résultats de mes tests

          Posté par  . Évalué à 2.

          Non, justement c'est le même cas que celui de mon 1er résultat : sous Linux, un programme win64 exécuté avec Wine me répondra que la commande ls n'existe pas et que la commande dir existe, donc ça ne me permet pas de savoir que l'OS sous-jacent est Linux.

          c'est plutot une bonne nouvelle puisque c'est justement le but de WINE que de faire croire à l'executable qu'il tourne sous Windows.

      • [^] # Re: Résultats de mes tests

        Posté par  . Évalué à 2.

        avec un simple ls ou un dir

        Raté.

        which dir
        /bin/dir

        • [^] # Re: Résultats de mes tests

          Posté par  . Évalué à 2.

          Pendant que j'y pense… il me semble bien que, en C, fopen( "c:/foo.bar", "r" ); fonctionne, alors, si ça se trouve, il n'y a juste pas besoin de s'embêter avec des \?

          • [^] # Re: Résultats de mes tests

            Posté par  . Évalué à 1.

            Oui, pareil en Fortran, mais le problème n'est pas dans l'ouverture des fichiers, il est dans le décodage des chaînes de caractères.
            J'ai des fichiers nommés par exemple ../rep1/rep2/foo.txt et j'ai besoin de séparer le chemin et le nom du fichier. Il faut donc que je cherche le 1er / dans la chaîne de caractères en partant de la droite. Il est plus simple de savoir a priori quel séparateur de fichier chercher que de systématiquement essayer les deux.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.