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

Outils Delphi Discussion :

Nettoyer les uses [FAQ]


Sujet :

Outils Delphi

  1. #1
    Membre averti

    Homme Profil pro
    Inscrit en
    Octobre 2003
    Messages
    908
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 908
    Points : 447
    Points
    447
    Par défaut Nettoyer les uses
    Salut tout le monde, est ce que quelqu un aurait le nom d un logiciel parformant qui permet de nettoyer les unitées inutilies dans les uses ??

  2. #2
    Expert confirmé

    Inscrit en
    Août 2006
    Messages
    3 942
    Détails du profil
    Informations forums :
    Inscription : Août 2006
    Messages : 3 942
    Points : 5 654
    Points
    5 654
    Par défaut
    Jai,

    Tu en as combien de milliers pour avoir besoin d'un outil ?

    Le meilleur, est de ne les mettre qu'en cas de réel besoin, et de les enlever immédiatement quand il n'y en a plus besoin en cas d'évolution du logiciel.
    Si les cons volaient, il ferait nuit à midi.

  3. #3
    Membre averti

    Homme Profil pro
    Inscrit en
    Octobre 2003
    Messages
    908
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 908
    Points : 447
    Points
    447
    Par défaut
    Le probleme c'est que quand tu écris un logiciel tu n'es pas forcement tout seul à y travailler dessus. Alors peut etre qu'un outil te semble exagéré, mais je te promet que cela a un réel interet.

    Merci quand meme

  4. #4
    Expert confirmé

    Inscrit en
    Août 2006
    Messages
    3 942
    Détails du profil
    Informations forums :
    Inscription : Août 2006
    Messages : 3 942
    Points : 5 654
    Points
    5 654
    Par défaut
    Seul ou à 2000, si tout le monde suit le même principe, ça ne posera pas de problème.

    En groupe, il y a forcément des règles à respecter, sinon je vous plains côté foutoir.

    Et ce n'est qu'une règle de plus, pas vraiment méchante, hein.


    Non, je ne connais pas d'outil pour faire ça. Un exercice pourrait être d'en développer un ?
    Si les cons volaient, il ferait nuit à midi.

  5. #5
    Expert éminent sénior

    Avatar de sjrd
    Homme Profil pro
    Directeur de projet
    Inscrit en
    Juin 2004
    Messages
    4 517
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : Suisse

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2004
    Messages : 4 517
    Points : 10 154
    Points
    10 154
    Par défaut
    J'ai ouï dire que cela existait dans la suite d'experts GExperts.
    sjrd, ancien rédacteur/modérateur Delphi.
    Auteur de Scala.js, le compilateur de Scala vers JavaScript, et directeur technique du Scala Center à l'EPFL.
    Découvrez Mes tutoriels.

  6. #6
    Membre confirmé Avatar de TryExceptEnd
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2006
    Messages
    501
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Octobre 2006
    Messages : 501
    Points : 574
    Points
    574
    Par défaut
    Voici un outil en freeware :
    http://www.peganza.com/#ICARUS
    Si vous êtes libre, choisissez le Logiciel Libre.

  7. #7
    Membre chevronné
    Avatar de Clorish
    Profil pro
    Inscrit en
    Juin 2003
    Messages
    2 474
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 2 474
    Points : 2 158
    Points
    2 158
    Par défaut
    Boah moi je supprime tous les uses et je reecrit ceux qui manquent a la compil
    Apres ... suffit de parcourir methodiquement les unitees

    Par contre, quel est l'interet de les supprimer vu que l'optimisation a la compilation n'importe que le code effectif. Donc toute unitee importee mais non utilisée ne sera pas presente dans l'executable.
    Enfin ca fait quand meme plus propre.

    Je met juste un bemol sur le point precedent, du fait que certaines unitees effectuent du code dans la section Initialization.
    Celles ci doivent etre utilisées avec attention
    On passe du temps a vous repondre, alors soyez sympas, passez du temps ..... a vous relire !
    --
    Pourquoi tant de haine pour cette pauvre aide Delphi ????
    Aiiimezzz laaaaa .... Si-Non-Cham-Pi-Gnon !!!
    --
    Pour plus de Renseignements : Venez me rejoindre sur Msn .... Promis je mords pas

  8. #8
    Membre du Club
    Inscrit en
    Mai 2002
    Messages
    67
    Détails du profil
    Informations forums :
    Inscription : Mai 2002
    Messages : 67
    Points : 68
    Points
    68
    Par défaut
    Bonjour,

    Personnellement j'utilise cnWizards de cnPack. Il s'agit d'un produit similaire à GExperts, mais qui me paraît plus complet. Paour ma part, il a remplacer gExperts.

    Dans ce produit, il existe un module qui permet de supprimer les unités non utilisées dans les uses.

    Mais en fait, il apporte beaucoup plus et aujourd'hui, j'aurais beaucoup de difficultés à m'en passer.

    Tu le trouveras en version cnWizards 0.8.2.322_RC1 à l'adresse suivante :

    http://www.cnpack.org/index.php?lang=en

    Ce produit est toujours en évolution, il est cependant très stable en cette version.

    Cordialement

  9. #9
    Membre averti

    Homme Profil pro
    Inscrit en
    Octobre 2003
    Messages
    908
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 908
    Points : 447
    Points
    447
    Par défaut
    Seul ou à 2000, si tout le monde suit le même principe, ça ne posera pas de problème.

    En groupe, il y a forcément des règles à respecter, sinon je vous plains côté foutoir.

    Et ce n'est qu'une règle de plus, pas vraiment méchante, hein.

    ARF .... je suis d acord, mais l'erreur est humaine et un oubli peut vite arrivé dans ce genre de situation. De plus un outil permet de gagner un peu de temps

  10. #10
    Membre expert
    Avatar de LadyWasky
    Femme Profil pro
    Inscrit en
    Juin 2004
    Messages
    2 932
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 53
    Localisation : France, Hauts de Seine (Île de France)

    Informations forums :
    Inscription : Juin 2004
    Messages : 2 932
    Points : 3 565
    Points
    3 565
    Par défaut
    Je confirme ce que te dis BRODU, il existe un tel utilitaire dans les cnPacks.

    Sinon, quand tu supprimes tes unités à la main, et que tu re-compiles, Delphi te remet dans le uses les unités dont il a besoin, enfin celles utilisées pour les composants visuels que tu as placées sur la fiche.

    Les unités du style strutils sont à rajouter à la main étant donné qu'elle ne contiennent pas le code de composants VCL.
    Bidouilleuse Delphi

  11. #11
    Membre éprouvé
    Avatar de CapJack
    Homme Profil pro
    Prof, développeur amateur vaguement éclairé...
    Inscrit en
    Mars 2004
    Messages
    624
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Prof, développeur amateur vaguement éclairé...
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2004
    Messages : 624
    Points : 988
    Points
    988
    Par défaut
    Rappelons quand même un principe que personne n'a pensé à rappeler : un compilateur digne de ce nom n'incorpore dans l'exécutable final que ce qui est utile ! Ceci s'applique d'ailleurs aux unités entières, mais aussi aux procédures et fonctions individuelles à l'intérieur d'une unité : seul le code effectivement utilisé est incorporé dans l'exécutable final.

    Alors, à quoi bon "purifier" les clauses uses ? Ça ne sert strictement à rien, à part perdre du temps. Je sais bien que le refactoring est à la mode, et que les dévelopeurs de tels outils doivent gagner leur croûte, mais n'utilisons ce genre d'outil que quand c'est vraiment nécessaire (consolidation avec try..except, collecte de chaînes pour une localisation, etc).

    Dans le cas présent, c'est vraiment une perte de temps.


    Petite expérience : compilez une application vide.

    Clause par défaut :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs;
    Résulat pour l'exécutable (Delphi 2005) : 375808 octets


    Purifions la clause uses, qui pour une appli vide n'a besoin que de Forms :

    Résulat pour l'exécutable (Delphi 2005) : 375808 octets


  12. #12
    Expert éminent sénior

    Avatar de sjrd
    Homme Profil pro
    Directeur de projet
    Inscrit en
    Juin 2004
    Messages
    4 517
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : Suisse

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2004
    Messages : 4 517
    Points : 10 154
    Points
    10 154
    Par défaut
    Ce que tu dis n'a que peu de sens, CapJack. Et je peux démonter ton argument en trois secondes.

    Dans Forms.pas, tu as :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    uses Messages, Windows, SysUtils, Classes, Graphics, Menus, Controls, Imm,
      ActnList, MultiMon, HelpIntfs;
    Et récursivement tu en as encore plus ! Quand tu ne mets que Forms.pas, il met de toutes façons toutes ces autres unités.

    Ensuite, comme l'a fait remarquer Clorish je crois, la simple présence de codes d'initialisation et de finalisation dans une unité lui donne le droit d'exister dès qu'elle se trouve quelque part dans les uses. Sinon comment fonctionnerait XPMan par exemple ?

    Ces considérations de liaison intelligente ne sont en général pas vraie. Seulement pour les routines qui ne sont effectivement jamais appelées. Mais comme la plupart des routines sont appelées par les méthodes de classes, qui, dû à leur enchevêtrement complexe, sont presque toujours liées, même les routines sont souvent liées.

    Si on peut se passer de certains uses, cela fait quand même une différence.

    Aussi, c'est beaucoup plus clair à la lecture du code source, si seules les unités nécessaires sont utilisées. Moi je pousse même le bouchon jusqu'à les mettre dans un ordre habituel.
    sjrd, ancien rédacteur/modérateur Delphi.
    Auteur de Scala.js, le compilateur de Scala vers JavaScript, et directeur technique du Scala Center à l'EPFL.
    Découvrez Mes tutoriels.

  13. #13
    Membre éprouvé
    Avatar de CapJack
    Homme Profil pro
    Prof, développeur amateur vaguement éclairé...
    Inscrit en
    Mars 2004
    Messages
    624
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Prof, développeur amateur vaguement éclairé...
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2004
    Messages : 624
    Points : 988
    Points
    988
    Par défaut
    Je retiens ton argument par rapport au croisement des unités. C'est vrai que mon exemple n'est pas très approfondi, mais c'était juste pour expliquer.

    Quant à l'incorporation systématique des unités ayant un bloc initialization...finalization, en es-tu vraiment certain ? XPMan est incorporé parce qu'il modifie un certain type de ressources, et que ce type de modification est validée (car l'OS est censé y faire référence).

    Mais quid d'une unité dont la clause init ne modifierait qu'un jeu de variables qui ne sont référencées nulle part ? Le compilateur, surtout dans ses versions récentes, est bien plus intelligent que tu ne sembles le croire, et je le prouve :

    à mon projet vide précédent, j'ajoute une unité :

    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
    unit Unit2;
     
    interface uses Dialogs;
     
    implementation
     
    var A : Integer;
        B : Single;
        C : Double;
        D : string;
     
    initialization
     
     A := 1254; // valeur jamais utilisée
     B := 3.52; // valeur jamais utilisée
     C := 78.5214; // valeur jamais utilisée
     D := 'bonjour'; // valeur jamais utilisée
     
    end.
    J'ajoute Unit2 dans la clause uses de la fiche, je compile et...

    Résulat pour l'exécutable (Delphi 2005) : 375808 octets, autrement dit, pas un octet de plus !

    Par contre, dès que j'ajoute ShowMessage(D) à la fin de la séquence d'initialisation, on passe à 402432 octets.

    Es-tu toujours convaincu que ce que je dis n'"a pas de sens" ?

  14. #14
    Expert éminent sénior

    Avatar de sjrd
    Homme Profil pro
    Directeur de projet
    Inscrit en
    Juin 2004
    Messages
    4 517
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : Suisse

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2004
    Messages : 4 517
    Points : 10 154
    Points
    10 154
    Par défaut
    Citation Envoyé par CapJack
    Quant à l'incorporation systématique des unités ayant un bloc initialization...finalization, en es-tu vraiment certain ? XPMan est incorporé parce qu'il modifie un certain type de ressources, et que ce type de modification est validée (car l'OS est censé y faire référence).
    Absolument certain. Je peux également citer les unités qui par leur simple présence, ressencent dans leur initialization une classe avec RegisterClass -> Toute la classe est liée, et avec elle il y a beaucoup de chances que le reste de l'unité y passe.
    Citation Envoyé par CapJack
    Mais quid d'une unité dont la clause init ne modifierait qu'un jeu de variables qui ne sont référencées nulle part ? Le compilateur, surtout dans ses versions récentes, est bien plus intelligent que tu ne sembles le croire, et je le prouve :
    Certes, mais si tu mets tes variables dans la partie interface ?
    En fait là le compilo remarque que ces variables ne sont jamais lues (il peut le faire car elles sont en implementation, donc "privées"). Il ne compile donc pas du tout les instructions qui les modifient. Du coup, il a une initialization qu'il ne compile donc pas non plus, et donc l'unité est vide -> qu'elle soit liée ou non, il n'y aura effectivement pas un octet de plus.
    Citation Envoyé par CapJack
    Es-tu toujours convaincu que ce que je dis n'"a pas de sens" ?
    J'avais pris la précaution de dire "n'a que peu de sens". Je sais bien que les lieurs sont intelligents aujourd'hui. Mais en pratique il y a peu de choses qu'ils peuvent réellement faire
    Quand on fait de petits tests comme cela, il faut toujours penser à vérifier le fait suivant : est-ce que ça arrive vraiment en pratique, le cas que je suis en train d'étudier ?
    sjrd, ancien rédacteur/modérateur Delphi.
    Auteur de Scala.js, le compilateur de Scala vers JavaScript, et directeur technique du Scala Center à l'EPFL.
    Découvrez Mes tutoriels.

  15. #15
    Membre éprouvé
    Avatar de CapJack
    Homme Profil pro
    Prof, développeur amateur vaguement éclairé...
    Inscrit en
    Mars 2004
    Messages
    624
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Prof, développeur amateur vaguement éclairé...
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2004
    Messages : 624
    Points : 988
    Points
    988
    Par défaut
    Citation Envoyé par sjrd
    Certes, mais si tu mets tes variables dans la partie interface ? En fait là le compilo remarque que ces variables ne sont jamais lues (il peut le faire car elles sont en implementation, donc "privées"). Il ne compile donc pas du tout les instructions qui les modifient. Du coup, il a une initialization qu'il ne compile donc pas non plus, et donc l'unité est vide -> qu'elle soit liée ou non, il n'y aura effectivement pas un octet de plus.
    Je reconnais avoir encore été maladroit, mais si je déplace les variables dans la partie interface, ça ne change rien ! Et c'est normal !

    Je crois vraiment que c'est toi qui interprètes mal les choses : tu ne fais référence qu'au compilateur, mais le comportement dont nous parlons ne concerne absolument pas le compilateur en lui-même, mais l'étape finale, le linker... c'est lui qui fait le tri au moment de l'assemblage final !

    Une fois déplacées les variables dans la partie interface, le compilateur compile tout, évidemment, là nous sommes d'accord. Il compile tout le code, il stocke tous les symboles dans le .dcu. Pas de problème. Or nous ne parlons pas de la taille du .dcu, mais de l'exécutable final.

    Et c'est au moment de l'assemblage final, au moment où le linker prend la main, que seuls les symboles et blocs de code référencés, quand bien même déjà compilés vont être incorporés à cette salade finale qu'est l'exécutable. C'est comme ça que ça marche ! L'optimisation en taille s'effectue en dernière étape, bien en aval de la compilation, et le format des unités Pascal est conçu pour... d'ailleurs ça marche même si l'on n'a pas le code source de l'unité.

  16. #16
    Expert éminent sénior

    Avatar de sjrd
    Homme Profil pro
    Directeur de projet
    Inscrit en
    Juin 2004
    Messages
    4 517
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : Suisse

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2004
    Messages : 4 517
    Points : 10 154
    Points
    10 154
    Par défaut
    Je suis bien au courant de comment fonctionne un compilateur/linker, pour "travailler" dans le domaine.

    Je dis seulement que, dans la plupart des cas réels, beaucoup des optimisations du linker sont : soit presque toujours faites, parce que ce sont des routines très rarement utilisées ; soit presque jamais faites, parce que l'enchevêtrement des appels de routines, des sections d'initialisation, et des informations de type à l'exécution, font qu'elles se recoupent toutes entre elles.

    Donc le lieur est important. Sans ces optimisations, les exécutables seraient beaucoup plus gros. Mais ce serait leur base qui serait beaucoup plus grosse. Tout ce qu'on ajoute à une application VCL en plus de la fiche vierge fait rarement le poids face à ce qui existe dès le début.
    sjrd, ancien rédacteur/modérateur Delphi.
    Auteur de Scala.js, le compilateur de Scala vers JavaScript, et directeur technique du Scala Center à l'EPFL.
    Découvrez Mes tutoriels.

  17. #17
    Membre éprouvé
    Avatar de CapJack
    Homme Profil pro
    Prof, développeur amateur vaguement éclairé...
    Inscrit en
    Mars 2004
    Messages
    624
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Prof, développeur amateur vaguement éclairé...
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2004
    Messages : 624
    Points : 988
    Points
    988
    Par défaut
    ...donc, dans ce cas ça ne sert bien à rien de nettoyer les clauses uses.

    CQFD.

  18. #18
    Membre averti

    Homme Profil pro
    Inscrit en
    Octobre 2003
    Messages
    908
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 908
    Points : 447
    Points
    447
    Par défaut
    Waow ... je demandais juste un outil pour me simplifier la tache MDR, je pensais pas que ça aller déchainer les passions

  19. #19
    Membre expert
    Avatar de TicTacToe
    Inscrit en
    Septembre 2005
    Messages
    1 940
    Détails du profil
    Informations personnelles :
    Âge : 51

    Informations forums :
    Inscription : Septembre 2005
    Messages : 1 940
    Points : 3 575
    Points
    3 575
    Par défaut
    Sinon il y a CNPack qui fonctionne assez bien la dessus, je l'ai utilisé pour certains projets.

    Sinon, il y a bel et bien un avantage à ne laisser que les unités dont on a besoin, mis à part que cela peut prendre de la place et du traitement à cause des initialization et finalization. (les initialization peuvent même être dangereuse si non souhaitées dans certains cas!)

    Un projet bouge et évolue, il se peut que l'on utilise un certains compo. pdt un temps, d'une certaine bibliotheque et qu'un beau jour on en change.
    -> chercher/remplacer (car le replace component est rarement fiable en cas d'héritage de form) dans les sources + DFM
    -> et là, si on laisse les uses comme ca parce finalement on s'en fout, on sera forcément embété un jour lors d'une réinstall de Delphi ou lors de la lecture du projet sur un autre poste (travail en équipe) (et ce remplacement de composant on l'aura forcément oublié, on sera plus dedans), les uses en trop seront très génant car les compos plus utilisés n'auront aucune raison d'avoir été installés.

    De plus, on peut aussi tomber sur des unités circulaires à force de laisser trainer des unités en trop.

    Bref, du ménage régulier dans les uses, n'est pas que esthétique, mais sert aussi à mieux comprendre un programme/ une unité à 1ere vue, et facilite sa maintenance. 6 mois après ne pas avoir à se dire, pourquoi cette unité est là ? sert-elle vraiment ?
    Enfin, ce n'est que mon avis ^^
    Section Delphi
    La mine d'or: La FAQ, les Sources

    Un développement compliqué paraitra simple pour l'utilisateur, frustrant non ?
    Notre revanche ? l'inverse est aussi vrai ;-)

  20. #20
    Membre averti

    Homme Profil pro
    Inscrit en
    Octobre 2003
    Messages
    908
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 908
    Points : 447
    Points
    447
    Par défaut
    en tout cas merci a tous pour vos infos

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. [TP] Problème avec les uses
    Par Sid ali dans le forum Turbo Pascal
    Réponses: 14
    Dernier message: 27/02/2006, 19h12
  2. [TP] Problème avec les uses en pascal
    Par ryma501 dans le forum Turbo Pascal
    Réponses: 3
    Dernier message: 15/02/2006, 22h09
  3. Réponses: 3
    Dernier message: 10/02/2006, 23h24
  4. Tester et nettoyer les favoris
    Par Furius dans le forum Autres Logiciels
    Réponses: 2
    Dernier message: 12/09/2005, 20h50
  5. [TogetherDesignerCE] Construire les Use case UML2
    Par jacma dans le forum Autres
    Réponses: 3
    Dernier message: 10/09/2004, 21h30

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