Archivo de la etiqueta: elguille.info

Usar una class library desde proyecto de .NET MAUI

Pues eso… algo tan simple como usar en .NET MAUI una DLL creada a partir de un proyecto del tipo class library, puede ser toda una odisea. Te lo explico para que te quede claro.

Como ya sabrás, puedes crear proyectos del tipo class library para añadirlos como referencia a otros proyectos que usen esa DLL o biblioteca de clases. Algo que es bastante común en cualquier aplicación para .NET ya sea .NET Framework como para .NET a secas incluido los proyectos para aplicaciones móviles con Xamarin.Forms.
En estes último, lo que se suele hacer es usar una DLL compilada para .NET Standard.
Hasta aquí todo bien.

La idea de usar una biblioteca de clases es para reutilizar el código en proyectos diferentes, es decir, creas la biblioteca de clases con cierta funcionalidad y esa misma biblioteca de clases la utilizas en proyectos diferentes. Al menos si esos tipos de proyectos son compatibles en el sentido de usar el mismo .NET.

Y como ahora estoy haciendo pruebas de Google Cloud Natural Language, pues pensé crear algún proyecto para .NET MAUI que usara esa API. Y como ya tenía el código de ciertas clases creado como proyecto DLL (Class Library) pensé agregar la referencia al proyecto de .NET MAUI y… ¡yaumate! (una expresión de mi zona que quiere decir algo así como… ¡tararí que te vi! o… ¡que te lo has creído!)

¡Y así fue! ¡Me lo creí! Pensaba que en .NET MAUI las cosas seguirían siendo como en el resto de .NET, pero no…

De hecho, hasta creé una class library usando la plantilla de MAUI, pero ni por esas… el proyecto de .NET MAUI nada más que daba errores de que no se podía tener referencia a esas clases definidas en la DLL (o class library).

La solución que tomé fue añadir directamente el código de esas clases en el mismo proyecto de .NET MAUI y así funcionó, pero no era eso lo que yo pretendía, ya que además del proyecto para .NET MAUI tenía otros proyectos: de tipo consola de Windows Forms para C# y Visual Basic y en todos ellos pretendía usar la misma DLL o biblioteca de clases.

Pero la solución buena ha sido creando una DLL (proyecto del tipo Class Library), crear un paquete de NuGet y usar ese paquete como referencia en lugar de una referencia al proyecto de tipo class library.

Decirte que esa referencia, al proyecto, sí que funciona en los proyectos de tipo consola o de tipo Windows Forms, tanto para VB como para C#, pero no si el proyecto es de .NET MAUI.

Te lo explico por si alguna vez te pasa esto… para que no te calientes la cabeza ni pierdas todo el tiempo que yo he perdido.

Y para muestra, el proyecto ElizaNET y el correspondiente Eliza MAUI (los enlaces van al repositorio de GitHub), que ambos usan una DLL compilada para .NET 7.0 y que funcionan a la perfección (salvo los bugs que se puedan producir en esa biblioteca de clases, que algunos pueden surgir).

Esos dos proyectos usan el código publicado en NuGet de Eliza gcnl Library que ahora va por la versión 1.0.2.

El código fuente de esa DLL (o paquete de NuGet) está en este enlace (dentro del repositorio ElizaNET).

Y esto es todo… espero que te sea de utilidad. Esa es la intención.

Nos vemos.
Guillermo

Ejemplos de Google Cloud Natural Language para consola y .NET MAUI

Pues eso… aquí te dejo el código de una clase (Frases) para analizar un texto usando la API de Google Cloud Natural Language y un par de proyectos para usar esa clase. Los proyectos son para una aplicación de consola y para dispositivos usando .NET MAUI. Todo el código está para C#.

No te voy a explicar mucho por aquí, salvo lo indicado en el siguiente párrafo, pero te dejo todo el código fuente (para C#) en este repositorio de GitHub.
También incluyo algunas explicaciones y problemas que he tenido para usar la clase en el proyecto para .NET MAUI (no muchos, pero…)

Algunos trucos en el código de .NET MAUI

Un par de cosas que siempre suelo poner en los proyectos para .NET MAUI, porque no tienen la misma funcionalidad que con Xamarin.Forms, son:

  1. Definir el tamaño de la ventana para Windows, ya que en .NET MAUI la ventana se muestra enorme y no recuerda el tamaño último.
    Esto lo hago en el constructor de la clase App.
  2. Hacer que el control Frame se vea al completo (no se corte por la parte inferior).
    Esto lo consigo si en el StackLayout usado después del Frame se le deja un margin mínimo de 2.

Además, he añadido el código para simular un Expander ver figura 1).
Este expander lo utilizo para mostrar u ocultar la lista de textos de prueba.

Otra cosa interesante es usar un objeto Task (usar otro proceso) cuando se pulsa en el botón de analizar, con idea que se muestre el texto mientras está analizando el texto y no se quede «congelada» la ventana.

Este es el código del evento Clicked del botón de analizar:

private async void BtnAnalizar_Clicked(object sender, EventArgs e)
{
    txtResultado.Text = "";

    string tmp = txtTexto.Text;
    if (string.IsNullOrEmpty(tmp))
    {
        MostrarAviso("Por favor indica el texto a analizar de al menos 3 caracteres", esError: true);
        txtTexto.Focus();
        return;
    }

    text = tmp;
    HabilitarBotones(false);

    await Task.Run(() =>
    {
        MostrarAviso("Analizando el texto...", esError: false);
        frase = Frases.Add(text);

        BtnMostrar2.Dispatcher.Dispatch(() =>
        {
            // Inicialmente mostrar todo sin tokens
            BtnMostrar2_Clicked(null, null);
        });
        QuitarAviso();
    });

    HabilitarBotones(true);

En los métodos llamados desde Task.Run se tienen en cuenta el Dispatcher de los controles que se modifican, con idea de que no den problemas al hacerlo entre hilos diferentes.

Este es el código de los métodos QuitarAviso y MostrarAviso que modifican una etiqueta y un StackLayout.

private void QuitarAviso()
{
    LabelAviso.Dispatcher.Dispatch(() => { LabelAviso.IsVisible = false; });
    grbAviso.Dispatcher.Dispatch(() => { grbAviso.BackgroundColor = Colors.Transparent; });
}

private void MostrarAviso(string aviso, bool esError)
{
    grbAviso.Dispatcher.Dispatch(() =>
    {
        if (esError)
        {
            grbAviso.BackgroundColor = Colors.Firebrick;
        }
        else
        {
            grbAviso.BackgroundColor = Colors.SteelBlue;
        }
    });
    LabelAviso.Dispatcher.Dispatch(() =>
    {
        LabelAviso.Text = aviso;
        LabelAviso.IsVisible = true;
    });

Algunas capturas

Aquí tienes un par de capturas de la app para .NET MAUI en funcionamiento, en la figura 1 está funcionando en Windows (usando el expander), en la figura 2 antes de poner el expander y en la figura 3 en un móvil con Android (antes de poner el expander), en iPhone no me funciona (tampoco el resto de los proyectos que tenía, así que, no he podido hacer captura).

Figura 1. En Windows con el expander

Figura 2. En Windows

Figura 3. En Android

Te recomiendo que leas el post anterior para ver cómo crear un cliente de Google Cloud Natural Language y poder usarlo en estos proyectos, en ese post indico que el código es para Visual Basic, pero los pasos a seguir son los mismos para Visual Basic que para C#.


Espero que te sea de utilidad.

Nos vemos.
Guillermo

Google Cloud Natural Language, ejemplo en Visual Basic .NET

Pues eso… aquí te dejo un ejemplo para usar las API de Google Cloud Natural Language, pero para Visual Basic .NET

Con esas API podrás analizar textos (también en español) y ver las palabras que la forman (tokens), su estructura sintáctica, etc.

Nota:
Para usar este código tendrás que crearte una cuenta en Google Cloud, generar una «key» para usarla y poco más, todos los pasos están explicados en este enlace (el código de ejemplo es para C#, pero te servirá.

Pasos para crear un proyecto usando dotnet (cli):

– Abre una ventana de consola (o terminal)
– Posicionarse en la carpeta donde crear el proyecto
– Crear el proyecto
dotnet new console -n <nombre-proyecto>
dotnet new console -lang VB -n <nombre-proyecto>
– Cambiar al directorio del proyecto
cd <nombre-proyecto>
– Añadir el paquete de Google Cloud Natural Language API
dotnet add package Google.Cloud.Language.V1
– Copiar el fichero key.json con las claves y permisos
– Ver estos pasos para crearla:
https://codelabs.developers.google.com/codelabs/cloud-natural-language-csharp#3
– En IAM, añadir la cuenta creada (incluida en el fichero key.json) en +OTORGAR ACCESO
Solo estará la principal y/o las otras añadidas
– Si se ha usado otra cuenta, estará en IAM>Cuentas de Servicio
– Modificar Program.cs (o Program.vb) para usar el código que accede a la API de Natural Language
– Ejecutar el código
dotnet run

Notas:
– Debes crear una variable de entorno en Windows, (lo puedes hacer desde la misma consola) indicando el path donde estará el fichero key.json.
– Lo que yo hago es copiar ese fichero en la carpeta del ejecutable y la variable de entorno la defino de esta forma:
set GOOGLE_APPLICATION_CREDENTIALS=key.json
– Puedes modificar el fichero del proyecto y añadir lo siguiente:

  <ItemGroup>
    <None Update="key.json">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </None>
  </ItemGroup>

El código de ejemplo

'--------------------------------------------------------------------------------
' Ejemplo de Google Cloud Natural Language en Visual Basic .NET (31/ene/23 18.50)
'
' (c)Guillermo Som (Guille), 2023
'--------------------------------------------------------------------------------

Imports System
Imports System.Text
Imports gcl = Google.Cloud.Language.V1
Imports Google.Protobuf.Collections
Imports Google.Cloud.Language.V1.AnnotateTextRequest.Types

'Namespace NaturalLanguageApiDemo
Class Program
    Shared client As gcl.LanguageServiceClient '?

    Shared Sub Main(args As String())
        'Dim text = "El 8 de Febrero voy en bici al Camino de Santiago desde Sarria ¿crees que aguantaré?"
        Dim text = "Probando Google Cloud Natural Language con VB.NET ¿Funcionará esto?"

        Console.WriteLine("Ejemplos de Google.Cloud.Language")
        Console.WriteLine()
        Console.WriteLine("Pruebas de Google Cloud Natural Language en Visual Basic .NET")
        Console.WriteLine()
        Console.WriteLine("  Creando el cliente...")
        client = gcl.LanguageServiceClient.Create()
        Console.WriteLine()

        Dim repitiendo As Boolean = False

        Do

            If repitiendo Then
                Console.WriteLine($"Última: '{text}'")
                Console.WriteLine("Indica la frase que quieres analizar (0 salir, [la última])")
            Else
                Console.WriteLine($"Predeterminada: '{text}'")
                Console.WriteLine("Indica la frase que quieres analizar (0 salir, [predeterminada])")
            End If

            Console.Write("> ")
            Dim resText = Console.ReadLine()

            If Not String.IsNullOrEmpty(resText) Then

                If resText = "0" Then
                    Exit Do
                End If

                text = resText
            End If

            Do
                Console.WriteLine($"Analizar: '{text}'")
                Console.Write("1- Todo con tokens, 2- Todo sin tokens, 3- Solo tokens, 0- nueva frase [2] ? ")
                resText = Console.ReadLine()
                Console.WriteLine()

                If String.IsNullOrEmpty(resText) Then
                    resText = "2"
                End If

                If resText = "1" Then
                    Analizar(text, conTokens:=True)
                ElseIf resText = "2" Then
                    Analizar(text, conTokens:=False)
                ElseIf resText = "3" Then
                    AnalizarTokens(text)
                ElseIf resText = "0" Then
                    Exit Do
                End If

                Console.WriteLine()
            Loop While True

            repitiendo = True
        Loop While True
    End Sub

    Private Shared Sub Analizar(text As String, conTokens As Boolean)
        If client Is Nothing Then
            client = gcl.LanguageServiceClient.Create()
        End If

        Dim document = gcl.Document.FromPlainText(text)
        Dim response As gcl.AnnotateTextResponse

        Try
            response = client.AnnotateText(document, New Features With {
                    .ExtractSyntax = True,
                    .ExtractEntities = True,
                    .ExtractDocumentSentiment = True,
                    .ExtractEntitySentiment = True,
                    .ClassifyText = True
                })
        Catch
            response = client.AnnotateText(document, New Features With {
                    .ExtractSyntax = True,
                    .ExtractEntities = True,
                    .ExtractDocumentSentiment = True,
                    .ExtractEntitySentiment = True
                })
        End Try

        Dim sentiment = response.DocumentSentiment
        Console.WriteLine($"Detected language: {response.Language}")
        Console.WriteLine($"Sentiment Score: {sentiment.Score}, Magnitude: {sentiment.Magnitude}")
        Console.WriteLine("***Entities:")
        Dim entity1 As gcl.Entity = Nothing

        For Each entity0 In response.Entities

            If entity1 Is Nothing Then
                entity1 = entity0
            Else
                If entity0.Equals(entity1) Then Continue For
            End If

            Console.WriteLine($"Entity: '{entity0.Name}'")
            Console.WriteLine($"  Type: {entity0.Type},  Salience: {CInt((entity0.Salience * 100))}%")

            If entity0.Mentions.Count > 0 Then
                Console.WriteLine($"  Mentions: {entity0.Mentions.Count}")

                For Each mention In entity0.Mentions
                    Console.Write($"    Text: '{mention.Text.Content}' (beginOffset: {mention.Text.BeginOffset}),")
                    Console.WriteLine($" Type: {mention.Type}, Sentiment: {mention.Sentiment}")
                Next
            End If

            If entity0.Metadata.Count > 0 Then
                Console.WriteLine($"  Metadata: {entity0.Metadata}")

                If entity0.Metadata.ContainsKey("wikipedia_url") Then
                    Console.WriteLine($"    URL: {entity0.Metadata("wikipedia_url")}")
                End If
            End If
        Next

        Console.WriteLine("***Categories:")

        For Each cat In response.Categories
            Console.WriteLine($"Category: '{cat.Name}' (Confidence: {cat.Confidence})")
        Next

        Console.WriteLine("***Sentences:")

        For Each sentence In response.Sentences
            Console.WriteLine($" Sentence.Text.Content: '{sentence.Text.Content}'")
            Console.WriteLine($"   Sentence.Text.BeginOffset: {sentence.Text.BeginOffset}")
            Console.WriteLine($" Sentence.Sentiment .Magnitude: {sentence.Sentiment.Magnitude}, .Score: {sentence.Sentiment.Score}")
        Next

        If conTokens Then
            Console.WriteLine("***Tokens:")

            For i As Integer = 0 To response.Tokens.Count - 1
                MostrarToken(i, response.Tokens, conContenido:=False)
            Next
        End If
    End Sub

    Private Shared Sub AnalizarTokens(text As String)
        If client Is Nothing Then
            client = gcl.LanguageServiceClient.Create()
        End If

        Dim document = gcl.Document.FromPlainText(text)
        Dim response As gcl.AnnotateTextResponse

        Try
            response = client.AnnotateText(document, New Features With {
                    .ExtractSyntax = True,
                    .ExtractEntities = True,
                    .ExtractDocumentSentiment = True,
                    .ExtractEntitySentiment = True,
                    .ClassifyText = True
                })
        Catch
            response = client.AnnotateText(document, New Features With {
                    .ExtractSyntax = True,
                    .ExtractEntities = True,
                    .ExtractDocumentSentiment = True,
                    .ExtractEntitySentiment = True
                })
        End Try

        AnalizarSentecias(response)
    End Sub

    Private Shared Sub AnalizarSentecias(self As gcl.AnnotateTextResponse)
        Dim index As Integer = 0

        For Each sentence In self.Sentences
            Dim content = sentence.Text.Content
            Dim sentence_begin = sentence.Text.BeginOffset
            Dim sentence_end = sentence_begin + content.Length - 1

            While index < self.Tokens.Count AndAlso self.Tokens(index).Text.BeginOffset <= sentence_end
                MostrarToken(index, self.Tokens, conContenido:=True)
                index += 1
            End While
        Next
    End Sub

    Private Shared Sub MostrarToken(nToken As Integer, tokens As RepeatedField(Of gcl.Token), Optional conContenido As Boolean = True)
        Dim token As gcl.Token = tokens(nToken)
        Console.WriteLine($"{nToken}- Token: Text.Content: '{token.Text.Content}', Lemma: '{token.Lemma}'")

        If token.DependencyEdge.Label = gcl.DependencyEdge.Types.Label.Root Then
            Console.Write($"  **DependencyEdge Label: {token.DependencyEdge.Label}")

            If token.DependencyEdge.HeadTokenIndex <> nToken Then
                Console.Write($", HeadTokenIndex: {token.DependencyEdge.HeadTokenIndex}")
            End If

            Console.WriteLine("**")
        Else
            Console.Write($"  DependencyEdge Label: {token.DependencyEdge.Label}, HeadTokenIndex: {token.DependencyEdge.HeadTokenIndex}")
            Dim tokenDependency = tokens(token.DependencyEdge.HeadTokenIndex)
            Console.WriteLine($" ('{tokenDependency.Text.Content}')")
        End If

        If conContenido Then
            Console.WriteLine($"  PartOfSpeech:")
            Console.Write($"    Tag: {token.PartOfSpeech.Tag},")
            Dim sb = New StringBuilder()

            If token.PartOfSpeech.Aspect <> gcl.PartOfSpeech.Types.Aspect.Unknown Then
                sb.Append($" (Aspect: {token.PartOfSpeech.Aspect},")

                If token.PartOfSpeech.[Case] <> gcl.PartOfSpeech.Types.[Case].Unknown Then
                    sb.Append($" Case: {token.PartOfSpeech.[Case]},")
                End If

                If token.PartOfSpeech.Form <> gcl.PartOfSpeech.Types.Form.Unknown Then
                    sb.Append($" Form: {token.PartOfSpeech.Form},")
                End If

                If sb.ToString().EndsWith(","c) Then
                    sb.Length -= 1
                End If

                sb.Append("),")
            End If

            If token.PartOfSpeech.Gender <> gcl.PartOfSpeech.Types.Gender.Unknown Then
                sb.Append($" (Gender: {token.PartOfSpeech.Gender},")

                If token.PartOfSpeech.Mood <> gcl.PartOfSpeech.Types.Mood.Unknown Then
                    sb.Append($" Mood: {token.PartOfSpeech.Mood},")
                End If

                If token.PartOfSpeech.Number <> gcl.PartOfSpeech.Types.Number.Unknown Then
                    sb.Append($" Number: {token.PartOfSpeech.Number},")
                End If

                If sb.ToString().EndsWith(","c) Then
                    sb.Length -= 1
                End If

                sb.Append("),")
            End If

            If token.PartOfSpeech.Proper <> gcl.PartOfSpeech.Types.Proper.Unknown Then
                sb.Append($" Proper: {token.PartOfSpeech.Proper}")
            End If

            If sb.ToString().Trim().Length > 0 Then
                Console.WriteLine(sb.ToString().TrimEnd(","c))
            End If

            sb.Clear()
            sb.Append("   ")

            If token.PartOfSpeech.Person <> gcl.PartOfSpeech.Types.Person.Unknown Then
                sb.Append($" Person: {token.PartOfSpeech.Person},")
            End If

            If token.PartOfSpeech.Reciprocity <> gcl.PartOfSpeech.Types.Reciprocity.Unknown Then
                sb.Append($" Reciprocity: {token.PartOfSpeech.Reciprocity},")
            End If

            If token.PartOfSpeech.Tense <> gcl.PartOfSpeech.Types.Tense.Unknown Then
                sb.Append($" Tense: {token.PartOfSpeech.Tense},")
            End If

            If token.PartOfSpeech.Voice <> gcl.PartOfSpeech.Types.Voice.Unknown Then
                sb.Append($" Voice: {token.PartOfSpeech.Voice}")
            End If

            If sb.ToString().Trim().Length > 0 Then
                Console.WriteLine(sb.ToString())
            End If
        Else
            Console.WriteLine($"  PartOfSpeech Aspect: {token.PartOfSpeech.Aspect}, Case: {token.PartOfSpeech.[Case]}, Form: {token.PartOfSpeech.Form}")
            Console.WriteLine($"  PartOfSpeech Gender: {token.PartOfSpeech.Gender}, Mood: {token.PartOfSpeech.Mood}, Number: {token.PartOfSpeech.Number}")
            Console.WriteLine($"  PartOfSpeech Person: {token.PartOfSpeech.Person}, Proper: {token.PartOfSpeech.Proper}")
            Console.WriteLine($"  PartOfSpeech Reciprocity: {token.PartOfSpeech.Reciprocity}, Tag: {token.PartOfSpeech.Tag}")
            Console.WriteLine($"  PartOfSpeech Tense:: {token.PartOfSpeech.Tense}, Voice: {token.PartOfSpeech.Voice}")
        End If
    End Sub
End Class
'End Namespace

Una captura

La aplicación en funcionamiento

Código fuente

El código fuente del ejemplo para Visual Basic .NET, así como el de C#, los puedes ver/descargar desde este repositorio en GitHub.

Y esto ha sido todo amigos… 😉

Nos vemos.
Guillermo

Algunos compiladores para FORTH

Pues eso, aquí te dejo una pequeña lista de compiladores gratuitos para FORTH, esta lista me la ha facilitado ChatGPT (DEC 15 Version), podría haberlo buscado en GOOGLE, pero… Más que nada, para ver si ChatGPT/OpenAI sabía algo de este vetusto lenguaje, que por lo que puedo ver aún sigue vigente, se ve que hay muchos jubilados por ahí… 😉

La pregunta ha sido:

Hola, ¿puedes decirme qué compiladores gratuitos hay para el lenguaje FORTH?

Y esta ha sido su respuesta:
(en itálica las faltas de ortografía)

Hola, sí, hay varios compiladores gratuitos para el lenguaje FORTH disponibles. Algunos ejemplos incluyen:

Gforth: Es un compilador FORTH estandar y gratuito disponible para una variedad de sistemas operativos, incluyendo Windows, Linux, macOS y varios sistemas Unix. Gforth es compatible con la norma FORTH-83, así como con varias extensiones.

SwiftForth: Es un compilador FORTH gratuito para Windows, macOS y Linux. Viene con una interfaz de línea de comandos y una ventana de depuración, así como una serie de herramientas de desarrollo adicionales, como un editor de código integrado y una ventana de registro de sistema.

pForth: es un compilador FORTH gratuito para Windows y Linux que tambien incluye una interfaz de línea de comandos y una ventana de depuración. Es fácil de usar y se puede ejecutar en una variedad de plataformas.

Win32Forth: es un compilador FORTH gratuito para Windows con una interfaz gráfica de usuario. También incluye una ventana de depuración, un editor de código integrado y una ventana de registro de sistema.

4tH es otro compilador FORTH gratuito y multi plataforma, con ejecutar tanto en Windows, Linux y macOS.

Es importante mencionar que estos son solo algunos ejemplos y puede haber muchos más opciones disponibles. Te recomendaría investigar un poco para encontrar el compilador que mejor se adapte a tus necesidades específicas.

La pregunta la hice en español y la respuesta la ha dado en español.
Pero le he dicho que lo traduzca al inglés.

Por favor, ¿puedes traducir tu respuesta al inglés? gracias

Y aquí su respuesta con la traducción dada por ChatGPT:

Sure, here is my response translated to English:

Hello, yes, there are several free compilers available for the FORTH language. Some examples include:

Gforth: It is a standard and free FORTH compiler available for a variety of operating systems, including Windows, Linux, macOS, and several Unix systems. Gforth is compliant with the FORTH-83 standard, as well as various extensions.

SwiftForth: It’s a free FORTH compiler for Windows, macOS, and Linux. It comes with a command-line interface and a debugging window, as well as a number of additional development tools, such as an integrated code editor and a system log window.

pForth: It’s a free FORTH compiler for Windows and Linux that also includes a command-line interface and a debugging window. It is easy to use and can be run on a variety of platforms.

Win32Forth: It’s a free FORTH compiler for Windows with a graphical user interface. It also includes a debugging window, an integrated code editor, and a system log window.

4tH is another free and multi-platform FORTH compiler, running in Windows, Linux and macOS.

It’s worth noting that these are just a few examples and there may be many more options available. I would recommend researching a bit to find the compiler that best suits your specific needs.

Aquí tienes los enlaces a las páginas principales y a las de descargas.

Gforth de GNU (descargar snapshots, manual actualizado en gforth.org)

SwiftForth de FORTH Inc. (descargar evaluación)

pForth (descargar y código fuente en GitHub)

Win32Forth (descargar en sourceforge)

4tH (descargar en sourceforge)

 

Nos vemos.
Guillermo

A la de una, a la de dos, a la de tres y a la de FORTH

Pues eso… que en estos días me ha dado por retomar un viejo amigo (lenguaje de programación) que usé por primera vez en los años 80 (sobre el 84 u 85) en mi Commodore-64, me refiero al lenguaje FORTH.

Ya no lo recuerdo, pero creo que era un «cartucho» que se insertaba en el C-64, el FORTH 64, de eso estoy seguro, porque he encontrado esta página con el manual y esa imagen (ver la captura de aquí abajo) sí que la recuerdo.

Manual de Forth 64 para Commodore

La cuestión es que, en aquellos años, el BASIC del Commodore, primero del VIC-20, después del C-64, era… ¿cómo decirlo? muy GO-TerO, es decir, que no tenía DO/LOOP, WHILE, UNTIL y lo que se hacía era el clásico GOTO.
Y este lenguaje, FORTH, lo recuerdo como el primero en el que no usé un GOTO, entre otras cosas porque no existe la palabra GOTO en Forth. 😉

La verdad es que no tengo nada de aquello, porque llegué hasta comprar un libro de Forth, pero para el BBC Micro, y de algo me sirvió. No recuerdo detalles, pero recuerdo que era «emocionante» programar en ese lenguaje, sobre todo en aquellos tiempos que lo más que teníamos a nuestra disposición eran los GOSUB, y eso de que pudieras crear tus propias palabras (WORD es el término de Forth para las «subrutinas»), pues… ¡era una pasada!

Hablando de DO, LOOP, WHILE y UNTIL y el Commodore 64, también por aquellos años ochenta me fabriqué en «código máquina» (CM para el C-64) unas extensiones para BASIC, entre ellas el DO-LOOP UNTIL y DO-LOOP WHILE. Con el C-64 y el C-128 (pero menos) hice cosillas «curiosas» en ensamblador para el Commodore 64, en fin… chocheando que está ya uno… jajaja… en fin-bis.

Volviendo al Forth, decirte que lo mismo publico algunas cosillas de las que estoy haciendo, en este caso ya son para ANS-FORTH, el estándar, que estoy haciendo con Gforth de GNU (abajo tienes el enlace) para no olvidarme… aunque ahora ha cambiado un poco, sigue siendo un «peñazo» el tema de los números y las cadenas…

Lo más curioso de este lenguaje es que usa la notación polaca inversa, y que todo lo maneja poniendo y sacando cosas de la pila (stack), que es de tipo LIFO (last-in first-out, el último en entrar es el primero en salir), de forma que si quieres sumar dos números lo harás de esta forma:

7 3 + y para mostrarlo escribes un punto para ver el resultado.

Y si quieres hacer algo como: (3 + 2 ) * 8, tendrás que hacerlo, por ejemplo, de esta otra:

8 2 3 + * .

Y te mostrará 40, 5 * 8.

Los pasos que sigue son:

1- Pone el 8 en la pila
2- Pone el 2 en la pila
3- Pone el 3 en la pila
4- Pone el + en la pila y
5- Suma dos valores que haya en la pila y pone el resultado (5)
6- Pone el * en la pila
7- Multiplica dos valores de la pila y pone el resultado (40)
8- El punto muestra el valor que haya en la pila.

En FORTH no se usan los paréntesis ni hay nivel de precedencia, todo se maneja con el orden en que esté en la pila.

El ejemplo anterior se puede escribir también así:

2 3 + 8 *

Imagina cómo lo hace en este caso.

Solo comentarte que cuando pulsas intro es cuando empieza la acción, es decir, no hace nada mientras estás escribiendo las cosas para que se pongan en la pila.

Como te comento abajo, los de GNU han hecho una versión de Gforth para Android (ver la captura con el clásico HOLA-MUNDO).

GFORTH funcionando en un Pixel 4a de Android

Y poco más puedo decirte, salvo ponerte el código del HOLA-MUNDO que he hecho para el Android 😉

: HOLA-MUNDO   ( -- muestra el mensaje )
    ." Hola Mundo de GFORTH!"
;

Ahí abajo te dejo unos cuantos enlaces sobre FORTH por si quieres echarle un vistazo.

Y esto es todo… ah, y ¡Felices Fiestas! aunque sea en el día de los Reyes Magos 😉

Nos vemos.
Guillermo

P.S.
Enlaces sobre FORTH:
Wiki de Forth para Commodore.
GForth de GNU (el que estoy usando ahora), también existe una versión para Android.
FORTH, Inc., entre otros del «inventor» de Forth.
Starting Forth, un libro online de Leo Brodie.
FORTH Standard, pues eso…
And so Forth, también te puede servir.

Hay muchos más en internet, me he quedado sorprendido de que haya tanto movimiento sobre FORTH.

Compilar código Java desde la línea de comandos

Pues eso… probando con el código que he hecho con Java (desde el IntelliJ IDEA) he querido compilar el código a .exe, pero no sé cómo hacerlo… solo me he quedado en crear un fichero .jar y ese poder ejecutarlo con el «java» desde la línea de comandos.

Versión de Java instalado

Al intentar hacerlo me he topado con un par de problemas, al menos desde mi máquina con el Visual Studio 2019 instalado (aparte del Visual Studio 2022), aclaro esto porque se ve que el Visual Studio 2019 tiene instalada una versión del JDK más antigua, concretamente la versión 1.8.0_302 y el código que he usado en las pruebas con records y expresiones swicht necesita al menos el JDK 17 (JDK = Java Development Kit).

Para poder usar el código que he publicado en varios de los repositorios (y los gists) de GitHub, instalé el JDK 19 (el último a la hora de escribir esto y también cuando empecé a hacer las pruebas en noviembre), pero la versión del JDK que está primero en la variable PATH es la carpeta bin de la versión 1.8, por tanto, al usar cualquiera de los comandos de Java (java para ejecutar los .jar o javac para compilar el código) me daba error.

Cuando se instala el JDK (al menos desde Visual Studio) se crea una variable de entorno que se llama JAVA_HOME con el path del directorio donde está instalado el JDK de Java.
En mi equipo el path indicado es: C:\Program Files\Microsoft\jdk-11.0.12.7-hotspot. Que como te puedes imaginar es la versión 11 del JDK. Esa versión tampoco soporta las expresiones swicht ni los records (aunque sí los tipos genéricos).

Así que… la solución es instalar un JDK que soporte esas dos «cosillas» que necesito en mi código.

Instalar un nuevo JDK de Java

Como la última versión que hay actualmente es la 19, esa es la que descargué (desde el propio IDE de IntelliJ IDEA), pero que puedes descargar desde la página de OpenJDK.

A fecha de hoy (27 de noviembre) el último JDK de Java es el 19, pero también puedes descargar versiones anteriores, aunque… ¿para qué? si con el último te aseguras que lo tienes todo 😉
Quiero comentarte que desde esa página puedes descargar la versión del JDK para distintos sistemas operativos. En mi caso descargué el de Windows.

Si quieres usar otra versión anterior, por ejemplo, la implementación del Java SE 17 también valdría, puedes hacerlo desde los enlaces de la parte izquierda de la página del enlace que te he puesto en el párrafo anterior.

Para usarlo, te descargas el zip, lo extraes en una carpeta (normalmente en la carpeta .jdks del directorio de %USERPROFILE% (la carpeta predeterminada del usuario de Windows).

En mi caso, ahí tengo el JDK 19 (instalado por IntelliJ IDEA) y el JDK 17 que me lo descargué para comprobar que con esa versión también funciona.

Lo que hice a continuación es añadir variables de entorno para acceder fácilmente a esos directorios, concretamente he creado dos variables de entorno, una para el JDK 19 y otra para el JDK 17.

Para hacer esto, abre las opciones avanzadas del sistema (Advanced System Settings) y acceder a las variables de entorno (Environment Variables).
Para acceder, pulsa con el botón secundario en This PC > Settings y en el panel derecho (estoy usando Windows 11) pulsa en Advanced System Settings o bien en buscar escribe «Advanced System Settings» o desde la línea de comandos (o desde run) escribe control sysdm.cpl y selecciona la ficha «Advanced»

Una vez tengas las variables de entorno (ver la figura 1), pulsa en el botón New de la parte superior y en el cuadro de diálogo escribe JAVA_HOME19 y en el path escribe el directorio en el que está ese JDK, en mi caso escribí %USERPROFILE%\.jdks\openjdk-19.0.1 y después se puso el path que corresponde.

Figura 1. Las variables de entorno

No es necesario que pongas los dos paths a los dos JDK, yo solo los he puesto para comprobar que la versión 17 también servía.

Una vez que tengas esto, abre la terminal o la línea de comandos y escribe %JAVA_HOME19%\bin\java --version para comprobar la versión del JDK de Java instalado.

Microsoft Windows [Version 10.0.22000.1281]
(c) Microsoft Corporation. All rights reserved.

C:\Users\Guille>%JAVA_HOME19%\bin\java --version
openjdk 19.0.1 2022-10-18
OpenJDK Runtime Environment (build 19.0.1+10-21)
OpenJDK 64-Bit Server VM (build 19.0.1+10-21, mixed mode, sharing)

Puedes escribir tanto java --version como java -version, el resultado prácticamente es el mismo, pero no igual; además de que -version se muestra en el stream de error y –version en el stream de salida.

Te aclaro que todo esto es por si el Java que tienes instalado es anterior a la versión 17, cosa que ocurrirá si tienes Visual Studio instalado y has indicado que quieres crear aplicaciones de Xamarin o .NET MAUI que instalan los JDK de Java.

En cualquier caso, antes de ahcer todo lo aquí explicado, comprueba con java -version (sin indicar ningún path y con un solo guión, ya que las versiones anteriores no reconocen el parámetro --version) qué versión tienes y si es la 17 o superior, pues… ¡todo hecho!

En mi caso me muestra esto, así que, necesito instalar un JDK más reciente.

Microsoft Windows [Version 10.0.22000.1281]
(c) Microsoft Corporation. All rights reserved.

C:\Users\Guille>java -version
openjdk version "1.8.0_302"
OpenJDK Runtime Environment (Temurin)(build 1.8.0_302-b08)
OpenJDK 64-Bit Server VM (Temurin)(build 25.302-b08, mixed mode)

Al compilar el código te explicaré para qué he creado la variable de entorno %JAVA_HOME19%, ya que no he querido añadir el path al directorio bin del JDK en la variable de entorno PATH para que no afecte a lo que Visual Studio necesita.

El código de prueba

Para esto que te comentaré en este post, voy a usar el código publicado en el gist Evaluar.java (gist = trozo de código publicado en GitHub).

En ese código, utilizo la expresión switch (no confundir con la sentencia switch) y los records con tipos genéricos para simular una tuple de 2 valores, (tuple = una estructura de datos que consiste en múltiples partes).

Nota:
En el lenguaje Java no existe el «tipo tuple» aunque hay implementaciones que, si lo tienen, como por ejemplo JavaTuples de Maven (en esta página puedes ver algunos ejemplos de JavaTuples), pero no en el JDK oficial (al menos a día de hoy).

Como en mi código necesitaba que una función devolviese dos valores, lo simulé con un record de tipo genérico. Este es el código:

/**                                                                             
 * Tuple de dos valores para usar al buscar un operador y la posición del mismo.
 *                                                                              
 * @param operador Un valor del tipo T1.                                        
 * @param position Un valor del tipo T2.                                        
 * @param <T1> El tipo (por referencia) del primer parámetro.                   
 * @param <T2> El tipo (por referencia) del segundo parámetro.                  
 */                                                                              
record TuplePair<T1, T2>(T1 operador, T2 position) {                            
}

Compilar el código JAVA y crear el fichero .jar

Descarga el fichero ese de prueba, en el que utilizo 3 clases (2 clases y un record) ambos definidos dentro de la clase Evaluar (fichero Evaluar.java) y cópialo en cualquier carpeta de tu equipo.

Todo lo que aquí te explico también serviría si la clase ConsoleColor está definida en un fichero independiente, aunque el record (TuplePair) debe estar definido dentro de la clase Evaluar, ya que se accede directamente a los campos privados del record en vez de a métodos públicos. Esos campos privados son accesibles desde la clase Evaluar porque el record está definido «dentro» de esa clase.

Compilar el código con javac

Para compilar el código usamos javac indicando los ficheros .java a compilar, opcionalmente le podemos indicar en qué directorio queremos que se creen los ficheros compilados (con extensión .class) y la codificación de esos ficheros, por ejemplo:

%JAVA_HOME19%\bin\javac -d .\ -encoding UTF-8 *.java

En este caso, los ficheros .java están en la misma carpeta desde la que se ejecuta javac.exe y le indicamos que la carpeta para los ficheros .class generados sean en la misma carpeta, (en realidad ese comando u opción -d lo podríamos haber obviado) y que el enconding de los ficheros es UTF-8.

Si el código de los ficheros .java está en otra carpeta, por ejemplo, la carpeta src dentro del directorio actual, y queremos que los ficheros .class se guarden en la carpeta actual, podríamos escribir:

%JAVA_HOME19%\bin\javac -d .\ -encoding UTF-8 .\src\*.java

Crear y usar el .jar ejecutable

Una vez generados los ficheros .class nos toca usar el comando jar.exe para crear un fichero .jar.

Por ejemplo:

%JAVA_HOME19%\bin\jar cf evaluar.jar *.class

Este comando crea el fichero .jar pero sin indicar dónde encontrar el método main.

Para probar el código compilado tendríamos que hacerlo de esta forma:

%JAVA_HOME19%\bin\java -cp evaluar.jar Evaluar 25+(2*3)+5!

Con esto le estamos indicando que utilice el fichero evaluar.jar, que el método main está en la clase Evaluar y por último los parámetros que queremos pasarle al programa.

Otra forma de usar el código compilado (sin necesidad de que el fichero .jar esté presente, aunque sí los ficheros .class) es:

%JAVA_HOME19%\bin\java Evaluar 25+(2*3)+5!

Aquí le indicamos que use el el método main de la clase Evaluar y pase los parámetros indicados como argumentos del programa.

Crear y usar el .jar con un manifiesto que indica dónde encontrar el método main

Pero lo mejor es crear un fichero de manifiesto donde se le indique al comando jar.exe dónde está (o cuál es) el método main que queremos usar.

Para ello creamos un fichero de texto con el siguiente contenido:

Main-Class: Evaluar

Después de Main-Class: indicaremos el nombre de la clase en la que está definido el método main. Recuerda que el nombre de la clase distingue entre mayúsculas y minúsculas.

Esto lo podemos crear directamente desde la línea de comandos escribiendo lo siguiente:

echo Main-Class: Evaluar > manifiesto.txt

Una vez que tenemos ese fichero del manifiesto, podemos crear el fichero .jar de esta forma:

%JAVA_HOME19%\bin\jar cvmf manifiesto.txt evaluar.jar *.class

Y para usarlo tendríamos que hacerlo de esta otra:

%JAVA_HOME19%\bin\java -jar evaluar.jar 25+(2*3)+5!

Si esto último lo hacemos con un .jar sin manifiesto obtendríamos el siguiente error:

no main manifest attribute, in evaluar.jar

Nota:
El nombre del fichero manifiesto puede ser el que quieras y con la extensión que quieras. Aunque el nombre recomendado es MANIFEST.MF.

De hecho, si abres un fichero .jar (que en realidad es como un fichero comprimido) puedes ver que la estructura en la que se incluye una carpeta llamada META-INF con el manifiesto, incluso si no indicamos un manifiesto esa carpeta y el correspondiente fichero MANIFEST.MF se crea, aunque sin indicar qué clase contiene el método main.

El contenido del MANIFEST.MF, del .jar que hemos generado, sería este si se ha indicado dónde está el método main):

Manifest-Version: 1.0
Main-Class: Evaluar
Created-By: 19.0.1 (Oracle Corporation)

Además de esa carpeta se incluyen todos los .class que se hayan generado (un .class por cada clase incluida en el código).

Y esto ha sido todo amigos… espero que te sirva…

Nos vemos.
Guillermo

Evaluar expresiones (código en Java y C#)

Pues eso… Sí, has leído bien el título: en Java. Y no en JavaScript (aparte de en C#).

Y es que hace unos días, empecé a estudiar esto de Java a raíz que mi hijo David está haciendo un curso de Java (back-end) y era por si le echaba una mano en algunos conceptos, ya que lo que él estudio en su día fue COBOL, algo de Pascal y algo de Visual Basic 6, y claro… los conceptos de POO y esas cosas, como que no las tiene claras.

Y para poder ayudarle (si es que así me lo hace saber), me puse a «trastear» en Java, y de camino he aprendido entre otras cosas, que hay diferencias, algunos grandes, en comparación con C# que es lo más parecido, (con Visual Basic ni te cuento).

Algunas de esas diferencias son a favor de Java, y la mayoría, a favor de C# o si lo prefieres a favor de .NET, seguramente porque lo conozco más.

Pero sea como sea, y peleándome un poco con el código y el IDE, que por cierto estoy usando uno que me parece muy bueno y es gratis: IntelliJ IDEA (Community Edition) de JetBrains, he conseguido hacer algo más o menos operativo y que me está enseñando algunas de esas cosas que ni siquiera sabía que C# las tenía y otras que al parecer C# tiene porque Java las tenía.

Lo que más he echado en falta son las «tuplas», aunque en este código de evaluar expresiones aritméticas he solventado con records, más que nada porque en algunas funciones necesitaba devolver dos valores (seguramente en Java habrá otra forma de hacerlo, pero no la he encontrado). Otra cosa que he echado en falta en Java es las estructuras (tipos de datos definibles, pero por valor).

 

No te voy a explicar «paso a paso» el código ni nada de eso (al menos por ahora), simplemente te voy a decir lo que este evaluador sabe hacer (creo que lo hace bien) y el código lo podrás ver en el repositorio de GitHub.

Quiero comentarte que primero hice una versión usando records para hacer las operaciones de suma, restas, multiplicación y división (basándome en un ejemplo de la documentación de Java), y el último es usando tipos double que además de esas cuatro operaciones hace un par de ellas más: módulo y factorial.

El enlace de GitHub o, mejor dicho, los enlaces son para el código basado en el tipo double, en esos repositorios tienes los enlaces al código fuente que usa los records, tanto para Java como para C#.

 

Lo que hace el evaluador de expresiones

  • Evalúa expresiones entre paréntesis (con varios niveles de anidación).
  • Evalúa primero los operadores multiplicativos (* y x para multiplicar, / y : para dividir y % para el módulo) y después los operadores aditivos (+ para sumar, – para restar).
  • La expresión puede tener espacios, pero al evaluarla se quitan, por tanto: «1 5 * 2» se convierte en «15*2».
  • Si hay un paréntesis de apertura precedido por un dígito o de un paréntesis de cierre, se considera una multiplicación y se pone el signo *.
  • Si hay un paréntesis de cierre seguido de un dígito o de un paréntesis de apertura, se considera una multiplicación y se pone el signo *.
  • Evalúa factoriales (usando el carácter «!»), tanto de números enteros positivos (naturales) como números negativos con parte decimal.
  • El valor de la factorial para números no naturales se calcula usando la función gamma basada en un código adaptado de un ejemplo de StackOverflow.

 

El código de Java y C# en GitHub

Estos son los enlaces del código fuente que tengo publicado en GitHub:

EvaluarExpresiones-java y EvaluarExpresiones-csharp.

Espero que te sea de utilidad.

Nos vemos.
Guillermo

Edad de elGuille.info es 2022 – 1996 = 26

Pues eso… 26 añitos de nada que el menda ese llamado «Guille» empezó su andadura por estas tierras sin tierra… o por esta maraña enredada… 😉

Lo de «edad de elguille.info» es porque, aunque ese nombre solo empezó a finales de 2005 (de noviembre de 1996 a finales de 2005 hubo otros nombres varios, desde carpetas en wcostasol.es hasta subdominios de costasol.net), y ahora el que «mola» es este blog, pero en el fondo todo es «el Guille punto info«.

Y espero seguir contando… jejeje

Nos vemos.
Guillermo

gsNotas para .NET

Pues eso… para que exista como entrada en el blog, ya que inicialmente lo he publicado como una página.

El código fuente está en este repositorio de GitHub y ahí iré indicando los cambios y novedades de la aplicación.

Está escrita enteramente en C#.

Espero que te sea de utilidad.

Nos vemos.
Guillermo

Errores de iOS con .NET MAUI

Pues eso… todo iba bien con las aplicaciones de .NET MAUI para iOS (después de muchos quebraderos de cabeza) y ahora, de buenas a primeras, empezó a pasar después de actualizar Visual Studio 2022 de la versión 17.3.5 a la 17.3.6.

Nota del 16-oct-22:

Solución a medias (que al menos me sirve para seguir probando las aplicaciones de .NET MAUI en iOS).

He usado el proyecto en otro equipo (un laptop/portátil) con Visual Studio 2022 Preview y ahí sí que ha funcionado todo.
Así de simple.
No es mi equipo habitual, y las copias que tengo no incluyen los ficheros de github, pero al menos me ha permitido seguir probando cosas.

Un consuelo como otro cualquiera. 😉

El error que me ha dado es este:

Error: CS1705 Assembly ‘Microsoft.Maui’ with identity ‘Microsoft.Maui, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’ uses ‘Microsoft.iOS, Version=16.0.0.0, Culture=neutral, PublicKeyToken=84e04ff9cfb79065’ which has a higher version than referenced assembly ‘Microsoft.iOS’ with identity ‘Microsoft.iOS, Version=15.4.300.0, Culture=neutral, PublicKeyToken=84e04ff9cfb79065’

He reportado el error, ya que lo que encontré no me solucionaba el problema.

Lo último que hice fue desinstalar totalmente el Visual Studio 2022 y después volver a instalarlo, antes hice un Rollback a la versión anterior, y probé con prácticamente todas las opciones de dotnet workload, pero sin ningún resultado favorable.

En el bug que reporté me decían que estaba duplicado y ahí he leído que aparte de desinstalar el Visual Studio, hay que desinstalar el SDK de .NET 6, esto último no lo he hecho pensando que se instalaba con el propio Visual Studio, lo mismo que el SDK de .NET 7 se instala con el Visual Studio 2022 Preview (que ya antes lo tuve que desinstalar para que las apps de .NET MAUI funcionaran sin darme error de que faltaban workloads.

Bueno, pues voy a ver si puedo desinstalar el .NET SDK aunque tenga que volver a instalar el Visual Studio al completo… 🙄

Ya te iré contando. 🤞🏻🙏🏻🤔

Pasos seguidos

Paso 1: Desinstalar el .NET SDK 6.0.305

Figura 1. Desinstalar el .NET SDK 6.0.305

Paso 2: Desinstalar el .NET SDK 6.0.402

Figura 2. Al desinstalar el .NET SDK 6.0.305 dice que nones

Como ves en la figura 2, al querer desinstalar el .NET SDK 6.0.402 me dice que no, que lo ha instalado el Visual Studio.

Después del paso 2, la cosa sigue igual.

Paso 3: Desinstalo los SDK de MAUI y .NET 6 desde el instalador de Visual Studio

Figura 3. Desinstalo desde el instalador de VS los SDK de MAUI y los de Android, iOS y normal

Ahora se supone que no podré crear nada… salvo la app de Windows.
A ver qué pasa…🤞🏻

Estos son los errores que me da el Visual Studio al no tener los SDK (figura 4), ni la app de Windows funciona (normal, no hay ningún SDK válido).

Figura 4. Los errores de Visual Studio al no tener los SDK.

Ahora probaré a volver a instalarlos. Aunque antes voy a reiniciar el equipo.

Paso 4: Después de reiniciar el equipo, quito todo del instalador de Visual Studio

Y ahora iré añadiendo los workloads y demás…

Figura 5. Quito todo y ahora a indicar lo que se debe instalar

Paso 5: Selecciono los Workloads en el instalador de Visual Studio

Figura 6. Selecciono los Workloads en el instalador de Visual Studio

Y compruebo que los SDK estén (figura 7)

Figura 7. Los componentes individuales al configurar los workloads

A ver qué pasa después de instalar… 🤞🏻🤔

Conclusión de estos 6 pasos

¡No hay que rascar! todo sigue igual de mal (con la app para iOS).
Las aplicaciones de Windows y Android van bien.

Ahora me toca quitar el SDK de iOS a ver qué pasa… Ya no sé si cruzar los dedos o levantar uno… 🤔🙄☝🏻 (pero no el central que no es plan, jajaja)

Paso 7: Quito el SDK de iOS

Con esto tampoco funcionará, pero a ver si lo puedo instalar usando dotnet workload.

Figura 8. Quito el SDK de iOS

Esto es lo que muestra dotnet --info antes de hacer nada.

E:\gsCodigo_00\Visual Studio\pruebas\MAUI\Trucos_MAUI>dotnet --info
.NET SDK (reflecting any global.json):
 Version:   6.0.402
 Commit:    6862418796

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.22000
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\6.0.402\

global.json file:
  E:\gsCodigo_00\Visual Studio\pruebas\MAUI\Trucos_MAUI\global.json

Host:
  Version:      6.0.10
  Architecture: x64
  Commit:       5a400c212a

.NET SDKs installed:
  3.1.424 [C:\Program Files\dotnet\sdk]
  5.0.303 [C:\Program Files\dotnet\sdk]
  5.0.406 [C:\Program Files\dotnet\sdk]
  5.0.408 [C:\Program Files\dotnet\sdk]
  5.0.413 [C:\Program Files\dotnet\sdk]
  6.0.402 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 3.1.18 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.19 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.23 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.28 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.30 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.15 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 6.0.10 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 3.1.18 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.19 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.23 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.28 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.30 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.12 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.15 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.10 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 3.1.18 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.1.19 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.1.23 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.1.28 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.1.30 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.9 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.12 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.15 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.4 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.10 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

Download .NET:
  https://aka.ms/dotnet-download

Learn about .NET Runtimes and SDKs:
  https://aka.ms/dotnet/runtimes-sdk-info

Paso 8: Usar dotnet workload restore con uno de los proyectos

Empezaré con el comando dotnet workload restore Trucos_MAUI.sln.
(Por eso estaba posicionado en el directorio con el proyecto de pruebas).

Y dice que:

E:\gsCodigo_00\Visual Studio\pruebas\MAUI\Trucos_MAUI>dotnet workload restore Trucos_MAUI.sln
Installing workloads:

Successfully installed workload(s) maui maui-android maui-ios maui-tizen.

A probar toca…

Ahora en el proyecto no me muestra las opciones de probar ni con iOS ni con Android y el que tiene una advertencia en las dependencias es el net6.0-android. En fin… 🙄

Paso 9: Instalo nuevamente los workloads (y SDKs) para MAUI (que se había desmarcado)

Paso 10: Instalo nuevamente el .NET 6.0 SDK (v6.0.305)

Figura 9. Instalo nuevamente el .NET SDK 6.0.305

Aunque antes voy a probar usando el que tenía antes (con el .NET 6.0.402) por si el instalador de Visual Studio lo ha dejado bien (cosa que dudo, pero… ¡hay que tener fe! 🙏🏻)

Sigue con la misma cantinela.

Paso 11: Cambio en global.json la versión de .NET SDK a la 6.0.305 en vez de la 6.0.402

Intentaré ponerlo para que lo use la solución (del proyecto) con el fichero global.json y este contenido:

{
  "sdk": {
    "version": "6.0.305"
  }
}

A ver qué pasa.

Lo suponía, ese SDK se incluía con el Visual Studio 17.2.9 y no incluye el .NET MAUI (que se incluyó con el Visual Studio 2022 17.3 😒

Por tanto, no se puede hacer nada ni con Windows siquiera… ¡Los milagros en Visual Studio no existen! 🤣

Paso 12: Vuelvo a poner en el global.json el .NET SDK 6.0.402

Y cuando abro el Visual Studio me sale esto:

Figura 10: Encima con cachondeo

Que tome una «survey» de mi experiencia con el desarrollo para iOS… si es que… 🙄

¡Hecho! Aunque, no hay mucho que decir… están más enfocados al emparejamiento con un Mac, que es lo que te permite distribuir las apps de iOS sin necesidad de tener el móvil junto a tu «computadora» para instalar la app.

Pero tampoco funciona… sigue con el mismo error.

Seguimos…

Paso 13: Vuelvo a probar con dotnet workload restore Trucos_MAUI.sln.

Pero nada… sigue igual.

Paso 14: En vista que el .NET SDK 6.0.305 no me vale, lo vuelvo a desinstalar

Y después de desinstalar este SDK que ya no me vale, no sé qué más hacer… 🤷🏻‍♂️

Nota del 13-oct-22 00.08:
Como consuelo me ha quedado que al menos las apps de Xamarin para iOS funcionan desde Visual Studio 2022, ya que en Visual Studio 2019 no pueden firmar la app… cosas que pasan…
Ver esto: Truco 3 de .NET MAUI.

Nos vemos.
Guillermo