From 68578a4bc3f1667530f674db8d01933775ebc4ff Mon Sep 17 00:00:00 2001 From: Ivan Komissarov Date: Fri, 10 Aug 2018 01:28:06 +0300 Subject: Add protobuf.cpp and protobuf.objc modules These modules implement google protobuf support for QBS for c++ and objective-c languages [ChangeLog] Added protobuf support for C++ and Objective-C. Fixes: QBS-563 Change-Id: I4bb7e0bdfc1e09ea26c0cd3d3739a741ff834e5d Reviewed-by: Christian Kandeler --- examples/examples.qbs | 2 + examples/protobuf/cpp/README.md | 13 +++ examples/protobuf/cpp/addressbook.qbs | 18 +++ examples/protobuf/cpp/main.cpp | 171 +++++++++++++++++++++++++++++ examples/protobuf/objc/README.md | 5 + examples/protobuf/objc/addressbook.qbs | 15 +++ examples/protobuf/objc/main.m | 171 +++++++++++++++++++++++++++++ examples/protobuf/shared/addressbook.proto | 51 +++++++++ 8 files changed, 446 insertions(+) create mode 100644 examples/protobuf/cpp/README.md create mode 100644 examples/protobuf/cpp/addressbook.qbs create mode 100644 examples/protobuf/cpp/main.cpp create mode 100644 examples/protobuf/objc/README.md create mode 100644 examples/protobuf/objc/addressbook.qbs create mode 100644 examples/protobuf/objc/main.m create mode 100644 examples/protobuf/shared/addressbook.proto (limited to 'examples') diff --git a/examples/examples.qbs b/examples/examples.qbs index 0e4e5052c..4e51bdbaf 100644 --- a/examples/examples.qbs +++ b/examples/examples.qbs @@ -62,5 +62,7 @@ Project { "helloworld-minimal/hello.qbs", "helloworld-qt/hello.qbs", "install-bundle/install-bundle.qbs", + "protobuf/cpp/addressbook.qbs", + "protobuf/objc/addressbook.qbs", ] } diff --git a/examples/protobuf/cpp/README.md b/examples/protobuf/cpp/README.md new file mode 100644 index 000000000..f0e1b1a93 --- /dev/null +++ b/examples/protobuf/cpp/README.md @@ -0,0 +1,13 @@ +### Addressbook c++ example + +This example shows how to build a cpp application that uses Google protobuf. + +In order to build this example, you'll need to have a protobuf headers and library installed in the system in locations where QBS can find them. + +On Linux, you can install a package to the system. + +On macOS, you can use brew or compile and install protobuf manually: +- to /usr/local/ +- to any folder, say /Users//protobuf. Then you'll need to set protobuf.libraryPath: "/Users//protobuf/lib" and protobuf.includePath: "/Users//protobuf/include" + +On Windows, you have to compile and install protobuf manually to any folder and use libraryPath and includePath as shown above diff --git a/examples/protobuf/cpp/addressbook.qbs b/examples/protobuf/cpp/addressbook.qbs new file mode 100644 index 000000000..bd5afd377 --- /dev/null +++ b/examples/protobuf/cpp/addressbook.qbs @@ -0,0 +1,18 @@ +import qbs + +CppApplication { + name: "addressbook_cpp" + consoleApplication: true + condition: protobuf.present + + Depends { name: "cpp" } + cpp.cxxLanguageVersion: "c++11" + + Depends { id: protobuf; name: "protobuf.cpp"; required: false } + + files: [ + "../shared/addressbook.proto", + "main.cpp", + "README.md", + ] +} diff --git a/examples/protobuf/cpp/main.cpp b/examples/protobuf/cpp/main.cpp new file mode 100644 index 000000000..703a9d302 --- /dev/null +++ b/examples/protobuf/cpp/main.cpp @@ -0,0 +1,171 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include +#include +#include + +#include "addressbook.pb.h" + +using google::protobuf::util::TimeUtil; + +int printUsage(char *argv0) +{ + std::cerr << "Usage: " << argv0 << "add|list ADDRESS_BOOK_FILE" << std::endl; + return -1; +} + +std::string readString(const std::string &promt) +{ + std::string result; + std::cout << promt; + std::getline(std::cin, result); + return result; +} + +// This function fills in a Person message based on user input. +void promptForAddress(tutorial::Person* person) +{ + std::cout << "Enter person ID number: "; + int id; + std::cin >> id; + person->set_id(id); + std::cin.ignore(256, '\n'); + + *person->mutable_name() = readString("Enter name: "); + + const auto email = readString("Enter email address (blank for none): "); + if (!email.empty()) + person->set_email(email); + + while (true) { + const auto number = readString("Enter a phone number (or leave blank to finish): "); + if (number.empty()) + break; + + tutorial::Person::PhoneNumber *phone_number = person->add_phones(); + phone_number->set_number(number); + + const auto type = readString("Is this a mobile, home, or work phone? "); + if (type == "mobile") + phone_number->set_type(tutorial::Person::MOBILE); + else if (type == "home") + phone_number->set_type(tutorial::Person::HOME); + else if (type == "work") + phone_number->set_type(tutorial::Person::WORK); + else + std::cout << "Unknown phone type. Using default." << std::endl; + } + *person->mutable_last_updated() = TimeUtil::SecondsToTimestamp(time(NULL)); +} + +// Iterates though all people in the AddressBook and prints info about them. +void listPeople(const tutorial::AddressBook& address_book) +{ + for (int i = 0; i < address_book.people_size(); i++) { + const tutorial::Person& person = address_book.people(i); + + std::cout << "Person ID: " << person.id() << std::endl; + std::cout << " Name: " << person.name() << std::endl; + if (person.email() != "") { + std::cout << " E-mail address: " << person.email() << std::endl; + } + + for (int j = 0; j < person.phones_size(); j++) { + const tutorial::Person::PhoneNumber& phone_number = person.phones(j); + + switch (phone_number.type()) { + case tutorial::Person::MOBILE: + std::cout << " Mobile phone #: "; + break; + case tutorial::Person::HOME: + std::cout << " Home phone #: "; + break; + case tutorial::Person::WORK: + std::cout << " Work phone #: "; + break; + default: + std::cout << " Unknown phone #: "; + break; + } + std::cout << phone_number.number() << std::endl; + } + if (person.has_last_updated()) { + std::cout << " Updated: " << TimeUtil::ToString(person.last_updated()) << std::endl; + } + } +} + +int main(int argc, char* argv[]) { + // Verify that the version of the library that we linked against is + // compatible with the version of the headers we compiled against. + GOOGLE_PROTOBUF_VERIFY_VERSION; + + if (argc != 3) + return printUsage(argv[0]); + + tutorial::AddressBook address_book; + + // Read the existing address book. + std::fstream input(argv[2], std::ios::in | std::ios::binary); + if (!input) { + std::cout << argv[2] << ": File not found." << std::endl; + } else if (!address_book.ParseFromIstream(&input)) { + std::cerr << "Failed to parse address book." << std::endl; + return -1; + } + + const std::string mode(argv[1]); + if (mode == "add") { + // Add an address. + promptForAddress(address_book.add_people()); + + if (!input) + std::cout << "Creating a new file." << std::endl; + + // Write the new address book back to disk. + std::fstream output(argv[2], std::ios::out | std::ios::trunc | std::ios::binary); + if (!address_book.SerializeToOstream(&output)) { + std::cerr << "Failed to write address book." << std::endl; + return -1; + } + } else if (mode == "list") { + listPeople(address_book); + } else { + return printUsage(argv[0]); + } + + // Optional: Delete all global objects allocated by libprotobuf. + google::protobuf::ShutdownProtobufLibrary(); + + return 0; +} diff --git a/examples/protobuf/objc/README.md b/examples/protobuf/objc/README.md new file mode 100644 index 000000000..c0fc7c0e5 --- /dev/null +++ b/examples/protobuf/objc/README.md @@ -0,0 +1,5 @@ +### Addressbook objc example + +This example shows how to build an objective-c application that uses Google protobuf. + +In order to build this example, you'll need to have a ProtocolBuffers library or framework installed in the system. diff --git a/examples/protobuf/objc/addressbook.qbs b/examples/protobuf/objc/addressbook.qbs new file mode 100644 index 000000000..f087113d9 --- /dev/null +++ b/examples/protobuf/objc/addressbook.qbs @@ -0,0 +1,15 @@ +import qbs + +CppApplication { + name: "addressbook_objc" + consoleApplication: true + condition: protobuf.present && qbs.targetOS.contains("darwin") + + Depends { name: "cpp" } + Depends { id: protobuf; name: "protobuf.objc"; required: false } + + files: [ + "../shared/addressbook.proto", + "main.m", + ] +} diff --git a/examples/protobuf/objc/main.m b/examples/protobuf/objc/main.m new file mode 100644 index 000000000..903799fca --- /dev/null +++ b/examples/protobuf/objc/main.m @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Ivan Komissarov +** Contact: abbapoh@gmail.com +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#import "Addressbook.pbobjc.h" + +#import + +int printUsage(char *argv0) +{ + NSString *programName = [[NSString alloc] initWithUTF8String:argv0]; + NSLog(@"%@", [[NSString alloc] initWithFormat:@"Usage: %@ add|list ADDRESS_BOOK_FILE", programName]); + [programName release]; + return -1; +} + +NSString *readString(NSString *promt) +{ + NSLog(@"%@", promt); + NSFileHandle *inputFile = [NSFileHandle fileHandleWithStandardInput]; + NSData *inputData = [inputFile availableData]; + NSString *result = [[[NSString alloc]initWithData:inputData encoding:NSUTF8StringEncoding] autorelease]; + result = [[result stringByTrimmingCharactersInSet: + [NSCharacterSet whitespaceAndNewlineCharacterSet]] autorelease]; + return result; +} + +// This function fills in a Person message based on user input. +void promptForAddress(Person* person) +{ + person.id_p = [readString(@"Enter person ID number:") intValue]; + person.name = readString(@"Enter name:"); + + NSString *email = readString(@"Enter email address (blank for none):"); + if ([email length] != 0) + person.email = email; + + while (true) { + NSString *number = readString(@"Enter a phone number (or leave blank to finish):"); + if ([number length] == 0) + break; + + Person_PhoneNumber* phoneNumber = [[Person_PhoneNumber alloc] init]; + phoneNumber.number = number; + + NSString *type = readString(@"Is this a mobile, home, or work phone?:"); + NSLog(@"\"%@\"", type); + if ([type compare:@"mobile"] == NSOrderedSame) + phoneNumber.type = Person_PhoneType_Mobile; + else if ([type compare:@"home"] == NSOrderedSame) + phoneNumber.type = Person_PhoneType_Home; + else if ([type compare:@"work"] == NSOrderedSame) + phoneNumber.type = Person_PhoneType_Work; + else + NSLog(@"%@", @"Unknown phone type. Using default."); + + [person.phonesArray addObject:phoneNumber]; + } +} + +// Iterates though all people in the AddressBook and prints info about them. +void listPeople(AddressBook *addressBook) +{ + NSArray *people = addressBook.peopleArray; + for (unsigned i = 0; i < [people count]; i++) { + Person *person = [people objectAtIndex:i]; + + NSLog(@"%@", [[[NSString alloc] initWithFormat:@"Person ID: %d", person.id_p] autorelease]); + NSLog(@"%@", [[[NSString alloc] initWithFormat:@"Person name: %@", person.name] autorelease]); + + if ([person.email length] != 0) { + NSLog(@"%@", [[[NSString alloc] initWithFormat:@"E-mail address: %@", person.email] autorelease]); + } + + NSArray *phones = person.phonesArray; + for (unsigned j = 0; j < [phones count]; j++) { + Person_PhoneNumber *phoneNumber = [phones objectAtIndex:j]; + NSString *phonePrefix; + + switch (phoneNumber.type) { + case Person_PhoneType_Mobile: + phonePrefix = @"Mobile phone"; + break; + case Person_PhoneType_Home: + phonePrefix = @"Home phone"; + break; + case Person_PhoneType_Work: + phonePrefix = @"Work phone"; + break; + default: + phonePrefix = @"Unknown phone"; + break; + } + + NSLog(@"%@", [[[NSString alloc] initWithFormat:@" %@ #: %@", phonePrefix, phoneNumber.number] autorelease]); + } + printf("\n"); + } +} + +int main(int argc, char *argv[]) +{ + if (argc != 3) + return printUsage(argv[0]); + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + AddressBook *addressBook;// = [AddressBook alloc]; + NSString *filePath = [[[NSString alloc] initWithUTF8String:argv[2]] autorelease]; + + // Read the existing address book. + NSData *data = [NSData dataWithContentsOfFile:filePath]; + if (!data) { + NSLog(@"%@", [[NSString alloc] initWithFormat:@"%@ : File not found.", filePath]); + addressBook = [[[AddressBook alloc] init] autorelease]; + } else { + NSError *error; + addressBook = [AddressBook parseFromData:data error:&error]; + if (!addressBook) { + NSLog(@"%@", @"Failed to parse address book."); + [pool drain]; + return -1; + } + } + + if (strcmp(argv[1], "add") == 0) { + // Add an address. + Person *person = [[Person alloc] init]; + promptForAddress(person); + [addressBook.peopleArray addObject:person]; + + if (!data) { + NSLog(@"%@", @"Creating a new file."); + } + [[addressBook data] writeToFile:filePath atomically:YES]; + } else if (strcmp(argv[1], "list") == 0) { + listPeople(addressBook); + } else { + [pool drain]; + return printUsage(argv[0]); + } + + [pool drain]; + return 0; +} diff --git a/examples/protobuf/shared/addressbook.proto b/examples/protobuf/shared/addressbook.proto new file mode 100644 index 000000000..b4b33b4c6 --- /dev/null +++ b/examples/protobuf/shared/addressbook.proto @@ -0,0 +1,51 @@ +// See README.txt for information and build instructions. +// +// Note: START and END tags are used in comments to define sections used in +// tutorials. They are not part of the syntax for Protocol Buffers. +// +// To get an in-depth walkthrough of this file and the related examples, see: +// https://developers.google.com/protocol-buffers/docs/tutorials + +// [START declaration] +syntax = "proto3"; +package tutorial; + +import "google/protobuf/timestamp.proto"; +// [END declaration] + +// [START java_declaration] +option java_package = "com.example.tutorial"; +option java_outer_classname = "AddressBookProtos"; +// [END java_declaration] + +// [START csharp_declaration] +option csharp_namespace = "Google.Protobuf.Examples.AddressBook"; +// [END csharp_declaration] + +// [START messages] +message Person { + string name = 1; + int32 id = 2; // Unique ID number for this person. + string email = 3; + + enum PhoneType { + MOBILE = 0; + HOME = 1; + WORK = 2; + } + + message PhoneNumber { + string number = 1; + PhoneType type = 2; + } + + repeated PhoneNumber phones = 4; + + google.protobuf.Timestamp last_updated = 5; +} + +// Our address book file is just one of these. +message AddressBook { + repeated Person people = 1; +} +// [END messages] -- cgit v1.2.3