Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
ohalligon committed Aug 27, 2012
0 parents commit 22488c3
Show file tree
Hide file tree
Showing 9 changed files with 572 additions and 0 deletions.
15 changes: 15 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Xcode
build/*
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
*.xcworkspace
!default.xcworkspace
xcuserdata
profile
*.moved-aside
29 changes: 29 additions & 0 deletions LICENCE
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/***********************************************************************************
*
* Copyright (c) 2012 Olivier Halligon
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
***********************************************************************************
*
* Any comment or suggestion welcome. Referencing this project in your AboutBox is appreciated.
* Please tell me if you use this class so we can cross-reference our projects.
*
***********************************************************************************/

48 changes: 48 additions & 0 deletions Plugins/Demo.xcpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>

<plugin id="com.niji.dsquare.plist-plugin.demo"
name="Demo plist Structure Definition"
xmlns:xcpp="urn:X-AliSoftware:xcodeplugin:preprocessor">

<extension id="com.niji.dsquare.plist.structure-definition.demo"
name="Demo plist">

<filename pattern="Demo*.plist" />

<!-- "_root_" is the magic name of the top-level property list element -->
<definition name="_root_" localizedString="Demo Schema" class="Dictionary">
<dictionaryKeys>
<key name="Name" localizedString="Inventory Name" class="String" use="required"/>
<key name="Items" localizedString="Inventory Items" class="Inventory" use="required"/>
</dictionaryKeys>
</definition>

<definition name="Inventory" class="Array" arrayElementClass="InventoryItem">
<arrayEntries>
<entry class="BookTypeElement"/>
<entry class="MovieTypeElement"/>
</arrayEntries>
</definition>

<definition name="InventoryItem" class="VariantDictionary" default="BookTypeElement">
<xcpp:variants variantKey="Kind" localizedString="Inventory Item Kind">
<xcpp:variant variantValue="BookType" localizedString="Book" identifyingKey="Title">
<key name="Title" localizedString="Book Title" class="String" use="required"/>
<key name="Author" localizedString="Book Author" class="String" use="optional"/>
<key name="ReleaseDate" localizedString="First Release Date" class="Date" use="optional"/>
</xcpp:variant>
<xcpp:variant variantValue="MovieType" localizedString="Movie" identifyingKey="Title">
<key name="Title" localizedString="Movie Title" class="String" use="required"/>
<key name="Director" localizedString="Director" class="String" use="optional"/>
<key name="ReleaseDate" localizedString="Air Date" class="Date" use="optional"/>
</xcpp:variant>
<xcpp:variant variantValue="CarType" localizedString="Car" identifyingKey="License">
<key name="License" localizedString="License Plate" class="String" use="required"/>
<key name="Brand" localizedString="Car Brand" class="String" use="optional"/>
</xcpp:variant>
</xcpp:variants>
</definition>

</extension>
</plugin>

105 changes: 105 additions & 0 deletions Plugins/TemplateInfoStructDef.xcpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<plugin name="File Template Plist Structure Definition"
id="com.apple.xcode.plist-plugin.filetemplate"
version="1.0"
xmlns:xc="urn:X-AliSoftware:xcodeplugin:preprocessor">

<extension point="com.apple.xcode.plist.structure-definition"
id="com.niji.dsquare.plist.structure-definition.filetemplate" version="1.0"
name="File Template plist">

<filename pattern="TemplateInfo.plist" />

<!-- "_root_" is the magic name of the top-level property list element -->
<definition name="_root_" localizedString="Template Description" class="Dictionary">
<dictionaryKeys>
<key name="Kind" localizedString="Template Type" class="TemplateKind" use="required"/>
<key name="AllowedTypes" localizedString="Allowed UTI file types" class="AllowedUTITypesList" use="required"/>
<key name="DefaultCompletionName" localizedString="Default completion name" class="String" use="required"/>
<key name="Description" localizedString="Template Description" class="String" use="optional"/>
<key name="MainTemplateFile" localizedString="Main template file" class="String" default="__FILEBASENAME__.m" use="required" />
<key name="Options" localizedString="Options" class="OptionsList" use="optional" />
<key name="Platforms" localizedString="Supported platforms" class="PlatformsList" use="required" />
<key name="SortOrder" localizedString="Sort order" class="String" />
<key name="Summary" localizedString="Template Summary" class="String" use="optional" />
</dictionaryKeys>
</definition>

<!-- Template Kind -->
<definition name="TemplateKind" class="String">
<allowableValues>
<value name="Xcode.IDEKit.TextSubstitutionFileTemplateKind"
localizedString="File Template with Text Substitution" />
</allowableValues>
</definition>

<!-- Allowed Types -->
<definition name="AllowedUTITypesList" class="Array" arrayElementClass="AllowedUTITypesEntry">
<arrayEntries>
<entry class="AllowedUTITypesEntry" localizedString="Objective-C Source" />
</arrayEntries>
</definition>
<definition name="AllowedUTITypesEntry" class="String">
<allowableValues>
<value name="public.objective-c-source" localizedString="Objective-C Source" />
<value name="public.objective-c-plus-plus-source" localizedString="Objective-C++ Source" />
</allowableValues>
</definition>

<!-- Platform -->
<definition name="PlatformsList" class="Array" arrayElementClass="PlatformsEntry">
<arrayEntries>
<entry class="PlatformsEntry" localizedString="iPhone OS" />
</arrayEntries>
</definition>
<definition name="PlatformsEntry" class="String">
<allowableValues>
<value name="com.apple.platform.iphoneos" localizedString="iPhone OS" />
<value name="com.apple.platform.macosx" localizedString="Mac OS" />
</allowableValues>
</definition>




<!-- Options -->
<definition name="OptionsList" class="Array" arrayElementClass="OptionsEntry" />

<xc:define id="CommonOptionProperties">
<key name="Identifier" localizedString="Identifier" class="String" use="required"/>
<key name="Description" localizedString="Description" class="String" use="required"/>
<key name="Name" localizedString="Option Name / Label" class="String" use="required"/>
<key name="Default" localizedString="Default value" class="String" use="required"/>
<key name="RequiredOptions" localizedString="Required Options to enabled this option" class="RequiredOptionsType"/>
</xc:define>

<definition name="OptionsEntry" class="VariantDictionary" default="OptionsEntry_Text">
<xc:variants variantKey="Type" localizedString="Type">
<xc:variant variantValue="text" localizedString="Text Field" identifyingKey="Identifier">
<xc:paste idref="CommonOptionProperties" />
<key name="Required" localizedString="Is required" class="Boolean" use="optional"/>
<key name="NotPersisted" localizedString="Does not persist for next templates" class="Boolean" use="optional"/>
</xc:variant>
<xc:variant variantValue="class" localizedString="Class Popup" identifyingKey="Identifier">
<xc:paste idref="CommonOptionProperties" />
<key name="Required" localizedString="Is required" class="Boolean" use="required"/>
<key name="FallbackHeader" localizedString="Fallback Header to import" class="String" use="required"/>
<key name="Suffixes" localizedString="File name suffixes for known classes" class="DictionaryOfStrings" use="required"/>
<key name="Values" localizedString="Known classes with custom Template variants" class="ArrayOfStrings" use="required"/>
</xc:variant>
<xc:variant variantValue="checkbox" localizedString="Boolean Option" identifyingKey="Identifier">
<xc:paste idref="CommonOptionProperties" />
</xc:variant>
</xc:variants>
</definition>


<!-- Other Global Definitions -->
<definition name="RequiredOptionsType" localizedString="Required Options to enable this option" class="Dictionary">
<!-- For each option identifier required, associate an NSArray of values that will enable this option -->
</definition>
<definition name="DictionaryOfStrings" class="Dictionary"/>
<definition name="ArrayOfStrings" class="NSArray" arrayElementClass="String"/>

</extension>

</plugin>
92 changes: 92 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
XCPC : Xcode Plugin Compiler
============================

This tool is intended to generate Xcode 4.x plugins, in dvtplugin file format, especially
to generate PLIST-file structure definitions.

These PLIST-file structure definitions define some well-known PLIST files (like Info.plist or Settings.bundle/Root.plist)
to make Xcode help you fill and edit these files, like:
* associating Human-Readable names for dictionary keys (that's how Xcode display human-readable keys for your Info.plist files)
* propose preformatted dictionary entries (that's how Xcode propose pre-formatted dicts in Settings.bundle/Root.plist files for the various kind of preferences entries (Text Field, ToggleSwitch...))
* Validate your PLIST conformity with the expected structure


## Generating your own PLIST structure definition files

To generate your own PLIST structure definition files, you have to:
* Prepare a XML file describing your PLIST expected structure. See [below](#xml-file-format).
* Preprocess and compile this XML file to generate a dvtplugin file. Use the xcpc shell script for that,
which will apply some XSLT stylesheet magic to the input XML file.

The xcpc shell script command line usage is as follow:
* Invoke `xcpc -xcodeplugin <inputFile>` to generate the output of the preprocessor to stdout. This will print the
XML corresponding to the old ".xcodeplugin" file format for Xcode plugins (Xcode3 file format)
* Invoke `xcpc -install <inputFile>` to generate the final ".dvtplugin" file (new Xcode4 file format) and make the script
install it directly into "~/Library/Developer/Xcode/Plug-Ins" so that is will be available the next time you restart Xcode.

## XML file format

The XML file format expected by xcpc is basically the xcodeplugin file format used in Xcode3.
xcpc will simply add:

* Support for useful tags to simplify the input format, especially for VariantDictionary used to propose pre-filled
dictionaries to your PLIST entries (like what is already done with the Settings.bundle/Info.plist files in Xcode to add
PreferenceSpecifier entries for either Text Fields, Toggle Switches, Titles, Sliders, ...) without have to define a lot of
repeatiting XML fragments; use `<xc:variant>` and `<xc:variant>` tags in your `<definition class="VariantDictionary">` nodes for that.
* Support for "copy/pasting" XML fragments (like #define macros in C). The principle is to associate an id to some XML fragment
so that you can "paste" it anywhere else in your XML without making a lot of copy/pasting. This is especially useful if you have
a lot of common dictionary keys in your PLIST structure and don't want to repeat these key definitions everywhere.
Use `<xc:define id="...">` and `<xc:paste idref="..." />` for that.

The XML xcodeplugin file format is actually not documented by Apple so this is mainly guessing and introspection.
The easiest way to discovery the syntax is to go and find inspiration in the existing xcodeplugin files
present in "Xcode.app/Contents/PlugIns" and in the examples provided with this project (see "Plugins" directory).

----

Here is the generic structure anyway:

<plugin name="Your Plist Structure Definition"
id="com.yourcompany.plist-plugin.yourformat"
version="1.0"
xmlns:xc="urn:X-AliSoftware:xcodeplugin:preprocessor">
<extension point="com.apple.xcode.plist.structure-definition"
id="com.yourcompany.plist.structure-definition.yourformat" version="1.0"
name="Your plist">
<definition ...>
<definition ...>
...
</extension>
</plugin>

Each `<definition>` tag define a type with its name, and a "class" which can be a standard class like "String", "Boolean",
"Dictionary", "Array" or "VariantDictionary", or any custom class whose definition is provided in another
`<definition name="...">` tag in the same XML.

Definitions of class "Array" can have an attribute "arrayElementClass" to define the class of the items in the array.
They can also have a subnode `<arrayEntries>` that contains some default entries listed using `<entry>` tags.

Definitions of class "String" can have a subnode `<allowableValues>` that contains a list of allowable values using `<value>` tags.

Definitions of class "Dictionary" should have a subnode `<dictionaryKeys>` that list the allows keys in this dictionary.
Each key is defined using a `<key name="..." localizedString="..." class="..." use="[optional|required]">` node.
Class can be "*" to allow the user to select any class instead of forcing a strict type for the key.

As VariantDictionary are quite more complex to define, the tags `<xc:variants>` and `<xc:variant>` can be
used. They will be and preprocessed by the xcppreprocessor.xslt stylesheet to generate the appropriate
`<variants>` and `<variant>` tags, but also all associated definitions, and all allowableValues for the
variantKey in each case automatically. See examples for inspiration.


## Usage in Xcode

Once you have compiled your own dvtplugin to describe your custom PLIST structure and it has been installed into
"~/Library/Developer/Xcode/Plug-Ins", simply open any PLIST file in Xcode.

Then if your plist is not recognized automatically to be of the expected structure
(which could be done by filename matching using the <filename pattern="..." /> tags in your source XML),
simply right-click anywhere of your PLIST and select the appropriate entry in the "Property List Type"
submenu _(You should find your custom type here)_.
25 changes: 25 additions & 0 deletions Test Files/DemoTest.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Name</key>
<string>Some test</string>
<key>Items</key>
<array>
<dict>
<key>Kind</key>
<string>BookType</string>
<key>Title</key>
<string>Peter Pan</string>
<key>Author</key>
<string>Riesling</string>
</dict>
<dict>
<key>Kind</key>
<string>CarType</string>
<key>License</key>
<string>1234 ABC 35</string>
</dict>
</array>
</dict>
</plist>
38 changes: 38 additions & 0 deletions xcpc/xcpc
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/bin/sh

if [ "$1" = "-install" ]; then
doInstall=1
mkdir -p "${outputDir}"
shift
elif [ "$1" = "-xcodeplugin" ]; then
onlyPreprocess=1
shift
fi
if [ "${1:0:1}" = "-" -o ! -n "$1" ]; then
# Remaining argument starts with "-" or is empty
echo "Usage: xcpc [-install|-xcodeplugin] inputFile"
exit 1
fi

inputFile=$1
if [ $doInstall ]; then
outputFile=~/Library/Developer/Xcode/Plug-Ins/$(basename $inputFile .xcpp).dvtplugin
else
outputFile=/dev/stdout
fi
xsltDir=$(dirname $0)

if [ $onlyPreprocess ]; then
/usr/bin/xsltproc "${xsltDir}/xcppreprocessor.xslt" "${inputFile}"
if [ $? -ne 0 ]; then
echo "Failed to preprocess plugin source."
fi
else
cat "${inputFile}" | /usr/bin/xsltproc "${xsltDir}/xcppreprocessor.xslt" - | /usr/bin/xsltproc "${xsltDir}/xcpcompiler.xslt" - >"${outputFile}"
if [ $? -eq 0 ]; then
echo "Plugin generated to: ${outputFile}. Please restart Xcode."
else
echo "Failed to generate plugin."
fi
fi

Loading

0 comments on commit 22488c3

Please sign in to comment.