IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Déploiement/Installation Python Discussion :

cxfreeze compilation sous Linux


Sujet :

Déploiement/Installation Python

  1. #1
    Membre expérimenté
    Avatar de Luke spywoker
    Homme Profil pro
    Etudiant informatique autodidacte
    Inscrit en
    Juin 2010
    Messages
    1 077
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Etudiant informatique autodidacte

    Informations forums :
    Inscription : Juin 2010
    Messages : 1 077
    Points : 1 742
    Points
    1 742
    Par défaut cxfreeze compilation sous Linux
    Content de trouver dans la distribution standart de Linux le logiciel cx-freeze qui permet de compiler du code python en un executable sous Linux:
    Je suis pris au dépourvue car celui-çi ne fonctionne pas pour le testscript que je lui ai fait compiler:
    Il génère un exécutable qui ne marche pas et j'ai bien sur des messages d'erreurs comme ceux-çi:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    Monnom@Monnom-destop:~$ cxfreeze test.py
    copying /usr/lib/pymodules/python2.6/cx_Freeze/bases/Console -> /home/Monom/dist/test
    copying /usr/lib/libpython2.6.so.1.0 -> /home/Monom/dist/libpython2.6.so.1.0
    Traceback (most recent call last):
      File "/usr/bin/cxfreeze", line 5, in <module>
        main()
      File "/usr/lib/pymodules/python2.6/cx_Freeze/main.py", line 170, in main
        freezer.Freeze()
      File "/usr/lib/pymodules/python2.6/cx_Freeze/freezer.py", line 405, in Freeze
        self._FreezeExecutable(executable)
      File "/usr/lib/pymodules/python2.6/cx_Freeze/freezer.py", line 173, in _FreezeExecutable
        exe.copyDependentFiles, scriptModule)
      File "/usr/lib/pymodules/python2.6/cx_Freeze/freezer.py", line 333, in _WriteModules
        initModule = finder.IncludeFile(initScript, "cx_Freeze__init__")
      File "/usr/lib/pymodules/python2.6/cx_Freeze/finder.py", line 386, in IncludeFile
        deferredImports)
      File "/usr/lib/pymodules/python2.6/cx_Freeze/finder.py", line 259, in _LoadModule
        module.code = compile(fp.read() + "\n", path, "exec")
    TypeError: compile() expected string without null bytes

    Il faut peut etre spécifier des options, ce que je n'ai pas fait ceçi étant optionels, afin que le logiciel fonctionne correctement.

    Le testscript:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    from Tkinter import *
     
    def test() :
      global b
      b.configure(text='It\'s compiled')
     
    a=Tk()
    b=Label(a, text='test', width=32)
    c= Button(a, text='try', command=test)
    d= Button(a, text='Off', command=a.destroy)
    b.pack()
    c.pack()
    d.pack()
    a.mainloop()
    Si quelqu'un peut m'aider sa serai sympa a faire marcher le programme correctement sa nous fera avancer.

    PS:Mes excuses: j'ai déja publier cette question dans la mauvaise carégorie.
    Pour faire tes armes:
    Use du présent pour construire ton futur sinon use de ce que tu as appris auparavant.
    Et sois toujours bien armé avant de te lancer.
    Le hasard ne sourit qu'aux gens préparés...
    Site: Website programmation international (www.open-source-projects.net)
    Site: Website imagerie 3D (www.3dreaming-imaging.net)
    Testez aux moins pendant une semaine l'éditeur avec terminaux intégrées it-edit Vous l'adopterai sûrement !
    FUN is HARD WORK !!!

  2. #2
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 461
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 461
    Points : 9 248
    Points
    9 248
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    Comme j'avais besoin de cx_freeze (Windows et Linux), je me suis donné une méthode de travail il y quelque temps. Elle est ici:

    http://python.jpvweb.com/mesrecettes...p?id=cx_freeze

    Je l'ai donc appliqué sur ton exemple, et le setup.py créé est le suivant:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
     
    #!/usr/bin/python
    # -*- coding: utf-8 -*-
     
    import sys, os
    from cx_Freeze import setup, Executable
     
    #############################################################################
    # preparation des options 
    path = sys.path.append("./")
    includes = []
    excludes = []
    packages = []
     
    options = {"path": path,
               "includes": includes,
               "excludes": excludes,
               "packages": packages
               }
     
    #############################################################################
    # preparation des cibles
    base = None
    if sys.platform == "win32":
        base = "Win32GUI"
     
    cible_1 = Executable(
        script = "test.py",
        base = base,
        compress = True,
        icon = None,
        )
     
    #############################################################################
    # creation du setup
    setup(
        name = "test_cx_freeze",
        version = "0.1",
        description = "simple test de cx_freeze avec Tkinter",
        author = "Tyrtamos",
        options = {"build_exe": options},
        executables = [cible_1]
        )
    Appliqué à ton code test.py:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
    #!/usr/bin/python
    # -*- coding: utf-8 -*-
     
    from Tkinter import *
     
    def test() :
        global b
        b.configure(text='It\'s compiled')
     
    a=Tk()
    b=Label(a, text='test', width=32)
    c= Button(a, text='try', command=test)
    d= Button(a, text='Off', command=a.destroy)
    b.pack()
    c.pack()
    d.pack()
    a.mainloop()
    Sur Ubuntu 2010.10 (Python 2.6.6), il faut alors se mettre en console dans le répertoire du code source test.py, qui contient aussi setup.py, et faire:

    Il vient de nombreuses lignes lors de l'exécution, et aucune erreur.

    Les fichiers créés sont dans le répertoire build/exe.linux-i686-2.6, et l'exécution du fichier test donne bien le résultat escompté. Il y a dans ce répertoire toutes les bibliothèques nécessaires à l'exécution 'standalone' du fichier test.

    Dans d'autres cas plus compliqués, les messages d'erreur permettent de corriger le setup.py, par exemple en ajoutant un module ou un chemin. On peut aussi avoir à corriger directement le code source du programme. En effet, à ma grande surprise, j'ai pu constater que de vraies erreurs de code, pourtant acceptées par l'exécution interprétée 'normale', étaient décelées comme erreur par cx_freeze, ce qui n'est pas plus mal car cela contribue au déverminage du programme.

    A noter que le même script setup.py sans modification permet de faire la même opération sur Windows.

    cx_freeze est vraiment un très bon produit (je n'en dirais pas autant de sa notice d'utilisation...).

    Ok?

    Tyrtamos

    PS: quand tu mets un code en ligne, utilise les tags de code (#), car sans l'indentation, le code Python est incompréhensible.
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  3. #3
    Membre expérimenté
    Avatar de Luke spywoker
    Homme Profil pro
    Etudiant informatique autodidacte
    Inscrit en
    Juin 2010
    Messages
    1 077
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Etudiant informatique autodidacte

    Informations forums :
    Inscription : Juin 2010
    Messages : 1 077
    Points : 1 742
    Points
    1 742
    Par défaut De cxfreeze vers pyinstaller
    Merci, si j'ai bien compris il faut construire un script de compilation comme dans py2exe auquelle je suis trop habituer par sa simpliciter d'utilisation et la bonne documentation. Et je n'ai pas vraiment eu le temps de casser les dents sur cx-freeze et te remercie de la réponses:... j'essairai cette solution.

    Mais dans mon désespoir et ma défaite face a cx-freeze je me suis tourner vers pyinstaller et lue la doc...
    -Je suis arriver a l'installer.
    -J'ai réussi a générer un specfile(fichier de compilation) mais au moment de faire l'opération de construction:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    Monnom@Monnom-desktop:~/pyinstaller-1.4$python Build.py compil_test.spec
    j'ai les message d'erreurs suivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    Traceback (most recent call last):
      File "Build.py", line 1160, in <module>
        main(args[0], configfilename=opts.configfile)
      File "Build.py", line 1148, in main
        build(specfile)
      File "Build.py", line 1111, in build
        execfile(spec)
    IOError: [Errno 2] No such file or directory: 'compil_test.spec'
    Pourtant le fichier compil_test.spec est dans le dossier de construction compil_test et ne génère pas de subdossier dist...

    Je vais me tourner vers ce qu'a dit Tyrtamos pour l'instant... Mon but n'étant que de créer un exécutable pour Linux pour un programme pas vraiment compliquer.

    Si quelqu'un sait pourquoi pyinstaller ne fonctionne pas comme dit dans la doc merci de nous le faire savoir.

    Le prog de tyrtamos fonctionne parfaitement, je le remercie, et je voulais lui demander si son prog va chercher le chemin vers les modules contenus dans le programme a compiler par la ligne:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    path = sys.path.append("./")
    ou si cxfreeze n'en a pas besoin ?
    Sympa on peut meme ajouter une icone.
    Pour faire tes armes:
    Use du présent pour construire ton futur sinon use de ce que tu as appris auparavant.
    Et sois toujours bien armé avant de te lancer.
    Le hasard ne sourit qu'aux gens préparés...
    Site: Website programmation international (www.open-source-projects.net)
    Site: Website imagerie 3D (www.3dreaming-imaging.net)
    Testez aux moins pendant une semaine l'éditeur avec terminaux intégrées it-edit Vous l'adopterai sûrement !
    FUN is HARD WORK !!!

  4. #4
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 461
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 461
    Points : 9 248
    Points
    9 248
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    Comme dit dans la doc, la valeur par défaut de path est sys.path. Je ne crois pas que l'ajout du répertoire de la source principale soit nécessaire ici. Mais tu peux essayer toi-même: met la ligne path=sys.path, ajoute un 2ème fichier python à importer dans le 1er et regarde si le script setup.py fonctionne toujours.

    Tyrtamos
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  5. #5
    Membre expérimenté
    Avatar de Luke spywoker
    Homme Profil pro
    Etudiant informatique autodidacte
    Inscrit en
    Juin 2010
    Messages
    1 077
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Etudiant informatique autodidacte

    Informations forums :
    Inscription : Juin 2010
    Messages : 1 077
    Points : 1 742
    Points
    1 742
    Par défaut
    Compiler avec cx_Freeze nécessite de créer un setup.py comme avec py2exe et j'ai réussi et si vous voulez compiler en multiplateforme polyarchitecturale il y a pyinstaller-1.5 qui fonctionne on ne peut simplement (pour son utilisation suivez le read_me c'est facile) car il génère un fichier de setup (*.spec) d'après les options fournis pour la création de celui-çi...
    Il ne faut pas oublier que pour créer des standalone sous Linux il faut installer les paquets -dev des modules qui font partie du programme: ces paquet sont la pour ça.
    Grand merci a Tyrtamos qui ma tirer de la poise avec cx_Freeze et maintenant je suis complètement compiler py2exe, cx_Freeze et pyinstaller je crois que je suis armer.
    Pour faire tes armes:
    Use du présent pour construire ton futur sinon use de ce que tu as appris auparavant.
    Et sois toujours bien armé avant de te lancer.
    Le hasard ne sourit qu'aux gens préparés...
    Site: Website programmation international (www.open-source-projects.net)
    Site: Website imagerie 3D (www.3dreaming-imaging.net)
    Testez aux moins pendant une semaine l'éditeur avec terminaux intégrées it-edit Vous l'adopterai sûrement !
    FUN is HARD WORK !!!

  6. #6
    Membre du Club
    Homme Profil pro
    Inscrit en
    Août 2011
    Messages
    57
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Août 2011
    Messages : 57
    Points : 46
    Points
    46
    Par défaut
    Bonjour,

    Je me permets de relancer le sujet:
    je suis sous python 3.3 64bits, et lorsque je compile un fichier avec cxfreeze, j'ai bien l'exe, mais celui-ci ne fonctionne pas...
    voici le code su setup.py:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    # -*-coding:Latin-1 -*
     
    import sys, os
    from cx_Freeze import setup, Executable
     
    #############################################################################
    # preparation des options 
    path = sys.path.append("./")
    includes = []
    excludes = []
    packages = []
     
    options = {"path": path,
               "includes": includes,
               "excludes": excludes,
               "packages": packages
               }
     
    #############################################################################
    # preparation des cibles
    base = None
    if sys.platform == "win32":
        base = "Win32GUI"
     
    cible_1 = Executable(
        script = "salut.py",
        base = base,
        compress = True,
        icon = None,
        )
     
    #############################################################################
    # creation du setup
     
     
    setup(
        name = "salut",
        version = "0.1",
        description = "Ce programme vous dit bonjour",
    	    author = "testeur",
        options = {"build_exe": options},
        executables = [cible_1],
    )
    le fichier salut.py:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    # -*-coding:Latin-1 -*
    """Ce fichier affiche simplement une ligne grâce à la fonction print."""
     
    import os
     
    print("Salut le monde !")
     
    # Sous Windows il faut mettre ce programme en pause (inutile sous Linux)
    os.system("pause")
    et enfin le résultat sur powershell:

    running build
    running build_exe
    creating directory build\exe.win-amd64-3.3
    copying C:\Python33\lib\site-packages\cx_Freeze\bases\Win32GUI.exe -> build\exe.win-amd64-3.3\salut.exe
    copying C:\WINDOWS\SYSTEM32\python33.dll -> build\exe.win-amd64-3.3\python33.dll
    *** WARNING *** unable to create version resource
    install pywin32 extensions first
    writing zip file build\exe.win-amd64-3.3\library.zip

    Name File
    ---- ----
    m BUILD_CONSTANTS
    m _bisect
    m _bz2 C:\Python33\DLLs\_bz2.pyd
    m _codecs
    m _codecs_cn
    m _codecs_hk
    m _codecs_iso2022
    m _codecs_jp
    m _codecs_kr
    m _codecs_tw
    m _collections
    m _datetime
    m _dummy_thread
    m _functools
    m _heapq
    m _imp
    m _io
    m _locale
    m _multibytecodec
    m _string
    m _strptime
    m _struct
    m _thread
    m _threading_local
    m _warnings
    m _weakref
    m _weakrefset
    m abc
    m argparse
    m base64
    m binascii
    m bisect
    m builtins
    m bz2
    m calendar
    m codecs
    P collections
    m collections.abc
    m contextlib
    m copy
    m copyreg
    m cx_Freeze__init__ C:\Python33\lib\site-packages\cx_Freeze\initscripts\Console3.py
    m datetime
    m dummy_threading
    P encodings
    m encodings.aliases
    m encodings.ascii
    m encodings.base64_codec
    m encodings.big5
    m encodings.big5hkscs
    m encodings.bz2_codec
    m encodings.charmap
    m encodings.cp037
    m encodings.cp1006
    m encodings.cp1026
    m encodings.cp1140
    m encodings.cp1250
    m encodings.cp1251
    m encodings.cp1252
    m encodings.cp1253
    m encodings.cp1254
    m encodings.cp1255
    m encodings.cp1256
    m encodings.cp1257
    m encodings.cp1258
    m encodings.cp424
    m encodings.cp437
    m encodings.cp500
    m encodings.cp65001
    m encodings.cp720
    m encodings.cp737
    m encodings.cp775
    m encodings.cp850
    m encodings.cp852
    m encodings.cp855
    m encodings.cp856
    m encodings.cp857
    m encodings.cp858
    m encodings.cp860
    m encodings.cp861
    m encodings.cp862
    m encodings.cp863
    m encodings.cp864
    m encodings.cp865
    m encodings.cp866
    m encodings.cp869
    m encodings.cp874
    m encodings.cp875
    m encodings.cp932
    m encodings.cp949
    m encodings.cp950
    m encodings.euc_jis_2004
    m encodings.euc_jisx0213
    m encodings.euc_jp
    m encodings.euc_kr
    m encodings.gb18030
    m encodings.gb2312
    m encodings.gbk
    m encodings.hex_codec
    m encodings.hp_roman8
    m encodings.hz
    m encodings.idna
    m encodings.iso2022_jp
    m encodings.iso2022_jp_1
    m encodings.iso2022_jp_2
    m encodings.iso2022_jp_2004
    m encodings.iso2022_jp_3
    m encodings.iso2022_jp_ext
    m encodings.iso2022_kr
    m encodings.iso8859_1
    m encodings.iso8859_10
    m encodings.iso8859_11
    m encodings.iso8859_13
    m encodings.iso8859_14
    m encodings.iso8859_15
    m encodings.iso8859_16
    m encodings.iso8859_2
    m encodings.iso8859_3
    m encodings.iso8859_4
    m encodings.iso8859_5
    m encodings.iso8859_6
    m encodings.iso8859_7
    m encodings.iso8859_8
    m encodings.iso8859_9
    m encodings.johab
    m encodings.koi8_r
    m encodings.koi8_u
    m encodings.latin_1
    m encodings.mac_arabic
    m encodings.mac_centeuro
    m encodings.mac_croatian
    m encodings.mac_cyrillic
    m encodings.mac_farsi
    m encodings.mac_greek
    m encodings.mac_iceland
    m encodings.mac_latin2
    m encodings.mac_roman
    m encodings.mac_romanian
    m encodings.mac_turkish
    m encodings.mbcs
    m encodings.palmos
    m encodings.ptcp154
    m encodings.punycode
    m encodings.quopri_codec
    m encodings.raw_unicode_escape
    m encodings.rot_13
    m encodings.shift_jis
    m encodings.shift_jis_2004
    m encodings.shift_jisx0213
    m encodings.tis_620
    m encodings.undefined
    m encodings.unicode_escape
    m encodings.unicode_internal
    m encodings.utf_16
    m encodings.utf_16_be
    m encodings.utf_16_le
    m encodings.utf_32
    m encodings.utf_32_be
    m encodings.utf_32_le
    m encodings.utf_7
    m encodings.utf_8
    m encodings.utf_8_sig
    m encodings.uu_codec
    m encodings.zlib_codec
    m errno
    m functools
    m genericpath
    m gettext
    m heapq
    m imp
    P importlib
    m importlib._bootstrap
    m importlib.machinery
    m io
    m itertools
    m keyword
    m linecache
    m locale
    m math
    m nt
    m ntpath
    m operator
    m optparse
    m os
    m posixpath
    m quopri
    m reprlib
    m salut__main__ salut.py
    m stat
    m string
    m stringprep
    m struct
    m sys
    m textwrap
    m threading
    m time
    m token
    m tokenize
    m traceback
    m types
    m unicodedata C:\Python33\DLLs\unicodedata.pyd
    m warnings
    m weakref
    m zipimport
    m zlib

    Missing modules:
    ? _frozen_importlib imported from importlib

    copying C:\Python33\DLLs\_bz2.pyd -> build\exe.win-amd64-3.3\_bz2.pyd
    copying C:\Python33\DLLs\unicodedata.pyd -> build\exe.win-amd64-3.3\unicodedata.pyd

    Bref, lorsque je veux executer salut.exe, la console plante... avec ce message:

    Traceback (most recent call last):
    File "<frozen importlib._bootstrap>", line 1462, in _install
    File "<frozen importlib._bootstrap>", line 1454, in _setup
    AttributeError: 'module' object has no attribute 'get_magic'
    Fatal Python error: Py_Initialize: importlib install failed

    Current thread 0x000004ac:


    Désolé pour la longueur...

  7. #7
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 461
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 461
    Points : 9 248
    Points
    9 248
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    @pierre3401:

    cx_freeze ne semble pas fonctionner avec Python 3.3 (http://sourceforge.net/p/cx-freeze/bugs/33/).

    Solution: revenir à Python 3.2.
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. erreur a la compilation sous linux
    Par superC dans le forum wxWidgets
    Réponses: 40
    Dernier message: 10/03/2007, 13h54
  2. Compiler sous linux
    Par d.w.d dans le forum Linux
    Réponses: 2
    Dernier message: 06/01/2007, 00h31
  3. [mono] Compilation sous linux, erreur dans windows
    Par AlexandreP dans le forum Mono
    Réponses: 6
    Dernier message: 18/08/2006, 20h56
  4. erreur de compilation sous linux
    Par petdelascar dans le forum C
    Réponses: 11
    Dernier message: 21/12/2005, 21h10
  5. pb de compilation sous linux
    Par prsieux dans le forum Linux
    Réponses: 10
    Dernier message: 20/12/2005, 18h49

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo